Default land/ocean mask

When crunching data with netCDF-SCM, we want to cut files into (at least) Northern/Southern Hemisphere, land/ocean boxes. However, we don’t always have access to land-surface fraction information from the raw model output. In these cases, we simply apply a default land/ocean mask instead. In this notebook, we show how this mask looks and how it was derived.

Imports

[1]:
import iris
import numpy as np
[2]:
from matplotlib import pyplot as plt
import iris.plot as iplt
import iris.quickplot as qplt

Default mask

Our default mask lives in netcdf_scm.masks. We can access it using netcdf_scm.masks.get_default_sftlf_cube.

[3]:
from netcdf_scm.weights import get_default_sftlf_cube
[4]:
default_sftlf = get_default_sftlf_cube()
[5]:
# NBVAL_IGNORE_OUTPUT
fig = plt.figure(figsize=(16, 9))
qplt.pcolormesh(default_sftlf,);
/data/ubuntu-znicholls/miniconda3/envs/netcdf-scm/lib/python3.9/site-packages/iris/coords.py:1192: UserWarning: Coordinate 'longitude' is not bounded, guessing contiguous bounds.
  warnings.warn('Coordinate {!r} is not bounded, guessing '
/data/ubuntu-znicholls/miniconda3/envs/netcdf-scm/lib/python3.9/site-packages/iris/coords.py:1192: UserWarning: Coordinate 'latitude' is not bounded, guessing contiguous bounds.
  warnings.warn('Coordinate {!r} is not bounded, guessing '
../_images/usage_default-land-ocean-mask_7_1.png
[6]:
zoomed = default_sftlf.extract(
    iris.Constraint(latitude=lambda cell: -45 < cell < -25)
    & iris.Constraint(longitude=lambda cell: 120 < cell < 160)
)
[7]:
# NBVAL_IGNORE_OUTPUT
fig = plt.figure(figsize=(16, 9))
qplt.pcolormesh(zoomed,);
/data/ubuntu-znicholls/miniconda3/envs/netcdf-scm/lib/python3.9/site-packages/iris/coords.py:1192: UserWarning: Coordinate 'longitude' is not bounded, guessing contiguous bounds.
  warnings.warn('Coordinate {!r} is not bounded, guessing '
/data/ubuntu-znicholls/miniconda3/envs/netcdf-scm/lib/python3.9/site-packages/iris/coords.py:1192: UserWarning: Coordinate 'latitude' is not bounded, guessing contiguous bounds.
  warnings.warn('Coordinate {!r} is not bounded, guessing '
../_images/usage_default-land-ocean-mask_9_1.png

Deriving the mask

To derive the mask, we simply use the mask from the IPSL-CM6A-LR model in CMIP6.

[8]:
source_file = "../../../tests/test-data/cmip6output/CMIP6/CMIP/IPSL/IPSL-CM6A-LR/historical/r1i1p1f1/fx/sftlf/gr/v20180803/sftlf_fx_IPSL-CM6A-LR_historical_r1i1p1f1_gr.nc"
[9]:
comp_cube = iris.load_cube(source_file)
/data/ubuntu-znicholls/miniconda3/envs/netcdf-scm/lib/python3.9/site-packages/iris/fileformats/cf.py:803: UserWarning: Missing CF-netCDF measure variable 'areacella', referenced by netCDF variable 'sftlf'
  warnings.warn(message % (variable_name, nc_var_name))
[10]:
# NBVAL_IGNORE_OUTPUT
fig = plt.figure(figsize=(16, 9))
qplt.pcolormesh(comp_cube);
/data/ubuntu-znicholls/miniconda3/envs/netcdf-scm/lib/python3.9/site-packages/iris/coords.py:1192: UserWarning: Coordinate 'longitude' is not bounded, guessing contiguous bounds.
  warnings.warn('Coordinate {!r} is not bounded, guessing '
/data/ubuntu-znicholls/miniconda3/envs/netcdf-scm/lib/python3.9/site-packages/iris/coords.py:1192: UserWarning: Coordinate 'latitude' is not bounded, guessing contiguous bounds.
  warnings.warn('Coordinate {!r} is not bounded, guessing '
../_images/usage_default-land-ocean-mask_13_1.png
[11]:
sample_points = [
    ("longitude", np.arange(0.5, 360, 1)),
    ("latitude", np.arange(-89.5, 90, 1)),
]
[12]:
comp_cube_interp = comp_cube.interpolate(sample_points, iris.analysis.Linear())
comp_cube_interp.attributes[
    "history"
] = "Interpolated to a 1deg x 1deg grid using iris.interpolate with linear interpolation"
comp_cube_interp.attributes[
    "title"
] = "Default land area fraction assumption in netcdf-scm. Base on {}".format(
    comp_cube_interp.attributes["title"]
)
[13]:
iris.save(comp_cube_interp, "default_weights.nc")
!ncdump -h default_weights.nc
netcdf default_weights {
dimensions:
        lat = 180 ;
        lon = 360 ;
        string8 = 8 ;
variables:
        float sftlf(lat, lon) ;
                sftlf:standard_name = "land_area_fraction" ;
                sftlf:long_name = "Land Area Fraction" ;
                sftlf:units = "%" ;
                sftlf:cell_methods = "area: mean" ;
                sftlf:coordinates = "type" ;
        double lat(lat) ;
                lat:axis = "Y" ;
                lat:units = "degrees_north" ;
                lat:standard_name = "latitude" ;
                lat:long_name = "Latitude" ;
        double lon(lon) ;
                lon:axis = "X" ;
                lon:units = "degrees_east" ;
                lon:standard_name = "longitude" ;
                lon:long_name = "Longitude" ;
        char type(string8) ;
                type:units = "1" ;
                type:standard_name = "area_type" ;
                type:long_name = "Land area type" ;

// global attributes:
                :CMIP6_CV_version = "cv=6.2.3.5-2-g63b123e" ;
                :EXPID = "historical" ;
                :NCO = "\"4.6.0\"" ;
                :activity_id = "CMIP" ;
                :branch_method = "standard" ;
                :branch_time_in_child = 0. ;
                :branch_time_in_parent = 21914. ;
                :contact = "ipsl-cmip6@listes.ipsl.fr" ;
                :creation_date = "2018-07-11T07:27:04Z" ;
                :data_specs_version = "01.00.21" ;
                :description = "Land Area Fraction" ;
                :dr2xml_md5sum = "f1e40c1fc5d8281f865f72fbf4e38f9d" ;
                :dr2xml_version = "1.11" ;
                :experiment = "all-forcing simulation of the recent past" ;
                :experiment_id = "historical" ;
                :forcing_index = 1 ;
                :frequency = "fx" ;
                :further_info_url = "https://furtherinfo.es-doc.org/CMIP6.IPSL.IPSL-CM6A-LR.historical.none.r1i1p1f1" ;
                :grid = "LMDZ grid" ;
                :grid_label = "gr" ;
                :history = "Interpolated to a 1deg x 1deg grid using iris.interpolate with linear interpolation" ;
                :initialization_index = 1 ;
                :institution = "Institut Pierre Simon Laplace, Paris 75252, France" ;
                :institution_id = "IPSL" ;
                :license = "CMIP6 model data produced by IPSL is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License (https://creativecommons.org/licenses). Consult https://pcmdi.llnl.gov/CMIP6/TermsOfUse for terms of use governing CMIP6 output, including citation requirements and proper acknowledgment. Further information about this data, including some limitations, can be found via the further_info_url (recorded as a global attribute in this file) and at https://cmc.ipsl.fr/. The data producers and data providers make no warranty, either express or implied, including, but not limited to, warranties of merchantability and fitness for a particular purpose. All liabilities arising from the supply of the information (including any liability arising in negligence) are excluded to the fullest extent permitted by law." ;
                :mip_era = "CMIP6" ;
                :model_version = "6.1.5" ;
                :name = "/ccc/work/cont003/gencmip6/p86caub/IGCM_OUT/IPSLCM6/PROD/historical/CM61-LR-hist-03.1910/CMIP6/ATM/sftlf_fx_IPSL-CM6A-LR_historical_r1i1p1f1_gr" ;
                :nominal_resolution = "250 km" ;
                :online_operation = "once" ;
                :parent_activity_id = "CMIP" ;
                :parent_experiment_id = "piControl" ;
                :parent_mip_era = "CMIP6" ;
                :parent_source_id = "IPSL-CM6A-LR" ;
                :parent_time_units = "days since 1850-01-01 00:00:00" ;
                :parent_variant_label = "r1i1p1f1" ;
                :physics_index = 1 ;
                :product = "model-output" ;
                :realization_index = 1 ;
                :realm = "atmos" ;
                :source = "IPSL-CM6A-LR (2017):  atmos: LMDZ (NPv6, N96; 144 x 143 longitude/latitude; 79 levels; top level 40000 m) land: ORCHIDEE (v2.0, Water/Carbon/Energy mode) ocean: NEMO-OPA (eORCA1.3, tripolar primarily 1deg; 362 x 332 longitude/latitude; 75 levels; top grid cell 0-2 m) ocnBgchem: NEMO-PISCES seaIce: NEMO-LIM3" ;
                :source_id = "IPSL-CM6A-LR" ;
                :source_type = "AOGCM BGC" ;
                :sub_experiment = "none" ;
                :sub_experiment_id = "none" ;
                :table_id = "fx" ;
                :title = "Default land area fraction assumption in netcdf-scm. Base on IPSL-CM6A-LR model output prepared for CMIP6 / CMIP historical" ;
                :tracking_id = "hdl:21.14100/cc6c4852-271d-4c5a-adc3-42530ef19550" ;
                :variable_id = "sftlf" ;
                :variant_label = "r1i1p1f1" ;
                :Conventions = "CF-1.7" ;
}
[14]:
# NBVAL_IGNORE_OUTPUT
fig = plt.figure(figsize=(16, 9))
qplt.pcolormesh(comp_cube_interp);
/data/ubuntu-znicholls/miniconda3/envs/netcdf-scm/lib/python3.9/site-packages/iris/coords.py:1192: UserWarning: Coordinate 'longitude' is not bounded, guessing contiguous bounds.
  warnings.warn('Coordinate {!r} is not bounded, guessing '
/data/ubuntu-znicholls/miniconda3/envs/netcdf-scm/lib/python3.9/site-packages/iris/coords.py:1192: UserWarning: Coordinate 'latitude' is not bounded, guessing contiguous bounds.
  warnings.warn('Coordinate {!r} is not bounded, guessing '
../_images/usage_default-land-ocean-mask_17_1.png
[15]:
comp_cube_regrid = comp_cube.regrid(default_sftlf, iris.analysis.Linear())

As expected, the default mask is more or less identical to the IPSL mask, even with regridding.

[16]:
# NBVAL_IGNORE_OUTPUT
fig = plt.figure(figsize=(16, 9))
qplt.pcolormesh((default_sftlf - comp_cube_regrid));
../_images/usage_default-land-ocean-mask_20_0.png