These examples are based on the CMOR repository’s examples/python directory. They use one shared CMIP7 user input file and load CMIP7 tables from a local clone of WCRP-CMIP/cmip7-cmor-tables.

Install CMOR with conda:

conda install -c conda-forge cmor

or with pip:

pip install cmor --extra-index-url https://pcmdi.github.io/cmor

Run the examples from a working directory that contains the cmip7-cmor-tables repository:

git clone https://github.com/WCRP-CMIP/cmip7-cmor-tables.git
python /path/to/example_01_usual_2d_field.py

The examples expect tables under ./cmip7-cmor-tables/tables. To use a different location, set CMOR_TABLES_PATH to the directory containing the CMIP7 table JSON files. Each example also accepts --output-dir to choose where CMOR writes NetCDF output.

Each example builds the CMIP7 compound variable name and uses it to read CMIP7_cell_measures.json and CMIP7_long_name_overrides.json before writing data.

CMOR Input Files

Click to expand shared JSON input
{
  "_AXIS_ENTRY_FILE": "CMIP7_coordinate.json",
  "_FORMULA_VAR_FILE": "CMIP7_formula_terms.json",
  "_cmip7_option": 1,
  "_controlled_vocabulary_file": "../tables-cvs/cmor-cvs.json",
  "activity_id": "CMIP",
  "archive_id": "WCRP",
  "calendar": "360_day",
  "experiment_id": "amip",
  "forcing_index": "f1",
  "frequency": "mon",
  "grid_label": "g999",
  "host_collection": "CMIP7",
  "initialization_index": "i1",
  "institution_id": "MOHC",
  "license_id": "CC-BY-4.0",
  "nominal_resolution": "100 km",
  "outpath": "output",
  "physics_index": "p1",
  "realization_index": "r1",
  "region": "glb",
  "source_id": "ACCESS-ESM1-6"
}

Example 1: Usual Treatment of a 2-D Field

Click to expand Python code
#!/usr/bin/env python3

from __future__ import annotations

import argparse
import json
import os
from pathlib import Path

import cmor
import numpy as np

EXAMPLE_DIR = Path(__file__).resolve().parent
RUN_DIR = Path.cwd()
DEFAULT_TABLES_PATH = RUN_DIR / "cmip7-cmor-tables" / "tables"
TABLES_PATH = Path(os.environ.get("CMOR_TABLES_PATH", DEFAULT_TABLES_PATH))
INPUT_PATH = EXAMPLE_DIR.parent / "CMIP7_input_example.json"


def configure(
    output_dir: Path,
    frequency: str = "mon",
    realization_index: str = "r1",
    forcing_index: str = "f1",
) -> None:
    output_dir.mkdir(parents=True, exist_ok=True)
    user_input = json.loads(INPUT_PATH.read_text())
    user_input["outpath"] = str(output_dir)
    user_input["frequency"] = frequency
    user_input["realization_index"] = realization_index
    user_input["forcing_index"] = forcing_index
    input_path = output_dir / "CMIP7_input_example.json"
    input_path.write_text(json.dumps(user_input, indent=2, sort_keys=True))
    cmor.setup(inpath=str(TABLES_PATH), netcdf_file_action=cmor.CMOR_REPLACE)
    cmor.dataset_json(str(input_path))


def apply_cmip7_variable_metadata(
    var_id: int,
    realm: str,
    table_entry: str,
    frequency: str,
    region: str,
) -> str:
    compound_name = ".".join(
        [realm] + table_entry.split("_") + [frequency, region]
    )

    with (TABLES_PATH / "CMIP7_cell_measures.json").open() as handle:
        cell_measures = json.load(handle)["cell_measures"]
    cmor.set_variable_attribute(
        var_id,
        "cell_measures",
        "c",
        cell_measures.get(compound_name, ""),
    )

    with (TABLES_PATH / "CMIP7_long_name_overrides.json").open() as handle:
        long_name_overrides = json.load(handle)["long_name_overrides"]
    if compound_name in long_name_overrides:
        cmor.set_variable_attribute(
            var_id,
            "long_name",
            "c",
            long_name_overrides[compound_name],
        )

    return compound_name


def write_example(output_dir: Path) -> str:
    configure(output_dir)
    cmor.load_table("CMIP7_ocean.json")
    lat_id = cmor.axis(
        "latitude",
        "degrees_north",
        coord_vals=np.array([10.0, 20.0, 30.0], dtype="d"),
        cell_bounds=np.array([5.0, 15.0, 25.0, 35.0], dtype="d"),
    )
    lon_id = cmor.axis(
        "longitude",
        "degrees_east",
        coord_vals=np.array([0.0, 90.0, 180.0, 270.0], dtype="d"),
        cell_bounds=np.array([-45.0, 45.0, 135.0, 225.0, 315.0], dtype="d"),
    )
    time_id = cmor.axis(
        "time",
        "days since 1979-01-01",
        coord_vals=np.array([15.5, 45.5], dtype="d"),
        cell_bounds=np.array([0.0, 31.0, 60.0], dtype="d"),
    )
    data = np.array(
        [
            [254.0895, 258.4085, 1.0e20, 258.7101],
            [258.6680, 258.2990, 1.0e20, 255.0432],
            [253.7254, 251.2460, 1.0e20, 255.4808],
            [254.0995, 258.5085, 1.0e20, 258.8101],
            [258.8680, 258.4990, 1.0e20, 255.2432],
            [254.0254, 251.5460, 1.0e20, 255.7808],
        ],
        dtype="f4",
    ).reshape(2, 3, 4)
    var_id = cmor.variable(
        "tos_tavg-u-hxy-sea",
        "degC",
        [time_id, lat_id, lon_id],
        missing_value=1.0e20,
    )
    apply_cmip7_variable_metadata(
        var_id,
        "ocean",
        "tos_tavg-u-hxy-sea",
        "mon",
        "glb",
    )
    cmor.write(var_id, data)
    path = cmor.close(var_id, file_name=True)
    cmor.close()
    return path


def main() -> None:
    parser = argparse.ArgumentParser(
        description="Write CMIP7 example 1 with CMOR."
    )
    parser.add_argument(
        "--output-dir",
        type=Path,
        default=Path(__file__).resolve().parent / "output",
    )
    args = parser.parse_args()
    print(write_example(args.output_dir))


if __name__ == "__main__":
    main()

Click to expand NetCDF dump
netcdf tos_tavg-u-hxy-sea_mon_glb_g999_ACCESS-ESM1-6_amip_r1i1p1f1_197901-197902 {
dimensions:
	time = UNLIMITED ; // (2 currently)
	lat = 3 ;
	lon = 4 ;
	bnds = 2 ;
variables:
	double time(time) ;
		time:bounds = "time_bnds" ;
		time:units = "days since 1979-01-01" ;
		time:calendar = "360_day" ;
		time:axis = "T" ;
		time:long_name = "Time Intervals" ;
		time:standard_name = "time" ;
	double time_bnds(time, bnds) ;
	double lat(lat) ;
		lat:bounds = "lat_bnds" ;
		lat:units = "degrees_north" ;
		lat:axis = "Y" ;
		lat:long_name = "Latitude" ;
		lat:standard_name = "latitude" ;
	double lat_bnds(lat, bnds) ;
	double lon(lon) ;
		lon:bounds = "lon_bnds" ;
		lon:units = "degrees_east" ;
		lon:axis = "X" ;
		lon:long_name = "Longitude" ;
		lon:standard_name = "longitude" ;
	double lon_bnds(lon, bnds) ;
	float tos(time, lat, lon) ;
		tos:standard_name = "sea_surface_temperature" ;
		tos:long_name = "Sea Surface Temperature" ;
		tos:units = "degC" ;
		tos:cell_methods = "area: mean where sea time: mean" ;
		tos:missing_value = 1.e+20f ;
		tos:_FillValue = 1.e+20f ;
		tos:cell_measures = "area: areacello" ;

// global attributes:
		:Conventions = "CF-1.12" ;
		:activity_id = "CMIP" ;
		:archive_id = "WCRP" ;
		:area_label = "sea" ;
		:branded_variable = "tos_tavg-u-hxy-sea" ;
		:branding_suffix = "tavg-u-hxy-sea" ;
		:creation_date = "2026-08-07T00:46:47Z" ;
		:data_specs_version = "MIP-DS7.1.0.0" ;
		:description = "Simulation of the climate of the recent past with prescribed sea surface temperatures and sea ice concentrations." ;
		:drs_specs = "MIP-DRS7" ;
		:experiment = "Simulation of the climate of the recent past with prescribed sea surface temperatures and sea ice concentrations." ;
		:experiment_id = "amip" ;
		:external_variables = "areacello" ;
		:forcing_index = "f1" ;
		:frequency = "mon" ;
		:grid_label = "g999" ;
		:history = "2026-08-07T00:46:47Z ; CMOR rewrote data to be consistent with CF-1.12 and CMIP7 data requirements." ;
		:horizontal_label = "hxy" ;
		:host_collection = "CMIP7" ;
		:initialization_index = "i1" ;
		:institution = "Met Office Hadley Centre" ;
		:institution_id = "MOHC" ;
		:license_id = "CC-BY-4.0" ;
		:mip_era = "CMIP7" ;
		:nominal_resolution = "100 km" ;
		:physics_index = "p1" ;
		:product = "model-output" ;
		:realization_index = "r1" ;
		:realm = "ocean" ;
		:region = "glb" ;
		:source = "ACCESS-ESM1-6: aerosol: classic; atmosphere: um7-3; land-surface: cable3; ocean-biogeochemistry: wombatlite; ocean: mom5; sea-ice: cice5" ;
		:source_id = "ACCESS-ESM1-6" ;
		:table_info = "Name: CMIP7_ocean.json; Creation Date:(2026-07-21 13:16:24) MD5:734c6f53560fa60b7c1c21b38e5b9a3f" ;
		:temporal_label = "tavg" ;
		:title = "ACCESS-ESM1-6 output prepared for CMIP7" ;
		:tracking_id = "hdl:21.14107/39cb4d40-2a52-4c7e-a53f-c6a925306c32" ;
		:variable_id = "tos" ;
		:variant_label = "r1i1p1f1" ;
		:vertical_label = "u" ;
		:license = "CC-BY-4.0; CMIP7 data produced by MOHC is licensed under a Creative Commons Attribution 4.0 International License (https://creativecommons.org/licenses/by/4.0). Consult https://wcrp-cmip.github.io/cmip7-guidance/docs/CMIP7/Guidance_for_users/#2-terms-of-use-and-citations-requirements for terms of use governing CMIP7 output, including citation requirements and proper acknowledgment. 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." ;
		:cmor_version = "3.15.2" ;
data:

 time = 15.5, 45.5 ;

 time_bnds =
  0, 31,
  31, 60 ;

 lat = 10, 20, 30 ;

 lat_bnds =
  5, 15,
  15, 25,
  25, 35 ;

 lon = 0, 90, 180, 270 ;

 lon_bnds =
  -45, 45,
  45, 135,
  135, 225,
  225, 315 ;

 tos =
  254.0895, 258.4085, _, 258.7101,
  258.668, 258.299, _, 255.0432,
  253.7254, 251.246, _, 255.4808,
  254.0995, 258.5085, _, 258.8101,
  258.868, 258.499, _, 255.2432,
  254.0254, 251.546, _, 255.7808 ;
}


Example 2: Usual Treatment of a 3-D Field on Pressure Levels

Click to expand Python code
#!/usr/bin/env python3

from __future__ import annotations

import argparse
import json
import os
from pathlib import Path

import cmor
import numpy as np

EXAMPLE_DIR = Path(__file__).resolve().parent
RUN_DIR = Path.cwd()
DEFAULT_TABLES_PATH = RUN_DIR / "cmip7-cmor-tables" / "tables"
TABLES_PATH = Path(os.environ.get("CMOR_TABLES_PATH", DEFAULT_TABLES_PATH))
INPUT_PATH = EXAMPLE_DIR.parent / "CMIP7_input_example.json"


def configure(
    output_dir: Path,
    frequency: str = "mon",
    realization_index: str = "r1",
    forcing_index: str = "f1",
) -> None:
    output_dir.mkdir(parents=True, exist_ok=True)
    user_input = json.loads(INPUT_PATH.read_text())
    user_input["outpath"] = str(output_dir)
    user_input["frequency"] = frequency
    user_input["realization_index"] = realization_index
    user_input["forcing_index"] = forcing_index
    input_path = output_dir / "CMIP7_input_example.json"
    input_path.write_text(json.dumps(user_input, indent=2, sort_keys=True))
    cmor.setup(inpath=str(TABLES_PATH), netcdf_file_action=cmor.CMOR_REPLACE)
    cmor.dataset_json(str(input_path))


def apply_cmip7_variable_metadata(
    var_id: int,
    realm: str,
    table_entry: str,
    frequency: str,
    region: str,
) -> str:
    compound_name = ".".join(
        [realm] + table_entry.split("_") + [frequency, region]
    )

    with (TABLES_PATH / "CMIP7_cell_measures.json").open() as handle:
        cell_measures = json.load(handle)["cell_measures"]
    cmor.set_variable_attribute(
        var_id,
        "cell_measures",
        "c",
        cell_measures.get(compound_name, ""),
    )

    with (TABLES_PATH / "CMIP7_long_name_overrides.json").open() as handle:
        long_name_overrides = json.load(handle)["long_name_overrides"]
    if compound_name in long_name_overrides:
        cmor.set_variable_attribute(
            var_id,
            "long_name",
            "c",
            long_name_overrides[compound_name],
        )

    return compound_name


def write_example(output_dir: Path) -> str:
    configure(output_dir)
    cmor.load_table("CMIP7_atmos.json")
    lat_id = cmor.axis(
        "latitude",
        "degrees_north",
        coord_vals=np.array([10.0, 20.0, 30.0], dtype="d"),
        cell_bounds=np.array([5.0, 15.0, 25.0, 35.0], dtype="d"),
    )
    lon_id = cmor.axis(
        "longitude",
        "degrees_east",
        coord_vals=np.array([0.0, 90.0, 180.0, 270.0], dtype="d"),
        cell_bounds=np.array([-45.0, 45.0, 135.0, 225.0, 315.0], dtype="d"),
    )
    time_id = cmor.axis(
        "time",
        "days since 1979-01-01",
        coord_vals=np.array([15.5, 45.5], dtype="d"),
        cell_bounds=np.array([0.0, 31.0, 60.0], dtype="d"),
    )
    plev_id = cmor.axis(
        "plev19",
        "Pa",
        coord_vals=np.array(
            [
                100000.0,
                92500.0,
                85000.0,
                70000.0,
                60000.0,
                50000.0,
                40000.0,
                30000.0,
                25000.0,
                20000.0,
                15000.0,
                10000.0,
                7000.0,
                5000.0,
                3000.0,
                2000.0,
                1000.0,
                500.0,
                100.0,
            ],
            dtype="d",
        ),
    )
    var_id = cmor.variable(
        "ta_tavg-p19-hxy-air",
        "K",
        [time_id, plev_id, lat_id, lon_id],
        missing_value=1.0e20,
    )
    apply_cmip7_variable_metadata(
        var_id,
        "atmos",
        "ta_tavg-p19-hxy-air",
        "mon",
        "glb",
    )
    data = np.linspace(
        250.0,
        275.0,
        2 * 19 * 3 * 4,
        dtype="f4",
    ).reshape(2, 19, 3, 4)
    data[0, 0, 0, 0] = np.float32(1.0e20)
    cmor.write(var_id, data)
    path = cmor.close(var_id, file_name=True)
    cmor.close()
    return path


def main() -> None:
    parser = argparse.ArgumentParser(
        description="Write CMIP7 example 2 with CMOR."
    )
    parser.add_argument(
        "--output-dir",
        type=Path,
        default=Path(__file__).resolve().parent / "output",
    )
    args = parser.parse_args()
    print(write_example(args.output_dir))


if __name__ == "__main__":
    main()

Click to expand NetCDF dump
netcdf ta_tavg-p19-hxy-air_mon_glb_g999_ACCESS-ESM1-6_amip_r1i1p1f1_197901-197902 {
dimensions:
	time = UNLIMITED ; // (2 currently)
	plev = 19 ;
	lat = 3 ;
	lon = 4 ;
	bnds = 2 ;
variables:
	double time(time) ;
		time:bounds = "time_bnds" ;
		time:units = "days since 1979-01-01" ;
		time:calendar = "360_day" ;
		time:axis = "T" ;
		time:long_name = "Time Intervals" ;
		time:standard_name = "time" ;
	double time_bnds(time, bnds) ;
	double plev(plev) ;
		plev:units = "Pa" ;
		plev:axis = "Z" ;
		plev:positive = "down" ;
		plev:long_name = "Pressure Levels (19)" ;
		plev:standard_name = "air_pressure" ;
	double lat(lat) ;
		lat:bounds = "lat_bnds" ;
		lat:units = "degrees_north" ;
		lat:axis = "Y" ;
		lat:long_name = "Latitude" ;
		lat:standard_name = "latitude" ;
	double lat_bnds(lat, bnds) ;
	double lon(lon) ;
		lon:bounds = "lon_bnds" ;
		lon:units = "degrees_east" ;
		lon:axis = "X" ;
		lon:long_name = "Longitude" ;
		lon:standard_name = "longitude" ;
	double lon_bnds(lon, bnds) ;
	float ta(time, plev, lat, lon) ;
		ta:standard_name = "air_temperature" ;
		ta:long_name = "Air Temperature" ;
		ta:units = "K" ;
		ta:cell_methods = "area: time: mean where air" ;
		ta:missing_value = 1.e+20f ;
		ta:_FillValue = 1.e+20f ;
		ta:cell_measures = "area: areacella" ;

// global attributes:
		:Conventions = "CF-1.12" ;
		:activity_id = "CMIP" ;
		:archive_id = "WCRP" ;
		:area_label = "air" ;
		:branded_variable = "ta_tavg-p19-hxy-air" ;
		:branding_suffix = "tavg-p19-hxy-air" ;
		:creation_date = "2026-08-07T00:46:48Z" ;
		:data_specs_version = "MIP-DS7.1.0.0" ;
		:description = "Simulation of the climate of the recent past with prescribed sea surface temperatures and sea ice concentrations." ;
		:drs_specs = "MIP-DRS7" ;
		:experiment = "Simulation of the climate of the recent past with prescribed sea surface temperatures and sea ice concentrations." ;
		:experiment_id = "amip" ;
		:external_variables = "areacella" ;
		:forcing_index = "f1" ;
		:frequency = "mon" ;
		:grid_label = "g999" ;
		:history = "2026-08-07T00:46:48Z ; CMOR rewrote data to be consistent with CF-1.12 and CMIP7 data requirements." ;
		:horizontal_label = "hxy" ;
		:host_collection = "CMIP7" ;
		:initialization_index = "i1" ;
		:institution = "Met Office Hadley Centre" ;
		:institution_id = "MOHC" ;
		:license_id = "CC-BY-4.0" ;
		:mip_era = "CMIP7" ;
		:nominal_resolution = "100 km" ;
		:physics_index = "p1" ;
		:product = "model-output" ;
		:realization_index = "r1" ;
		:realm = "atmos" ;
		:region = "glb" ;
		:source = "ACCESS-ESM1-6: aerosol: classic; atmosphere: um7-3; land-surface: cable3; ocean-biogeochemistry: wombatlite; ocean: mom5; sea-ice: cice5" ;
		:source_id = "ACCESS-ESM1-6" ;
		:table_info = "Name: CMIP7_atmos.json; Creation Date:(2026-07-21 13:16:24) MD5:28339aa355908b9331150e1536f5e384" ;
		:temporal_label = "tavg" ;
		:title = "ACCESS-ESM1-6 output prepared for CMIP7" ;
		:tracking_id = "hdl:21.14107/480e7c54-803d-4c73-9ffd-62eb07c5d9dc" ;
		:variable_id = "ta" ;
		:variant_label = "r1i1p1f1" ;
		:vertical_label = "p19" ;
		:license = "CC-BY-4.0; CMIP7 data produced by MOHC is licensed under a Creative Commons Attribution 4.0 International License (https://creativecommons.org/licenses/by/4.0). Consult https://wcrp-cmip.github.io/cmip7-guidance/docs/CMIP7/Guidance_for_users/#2-terms-of-use-and-citations-requirements for terms of use governing CMIP7 output, including citation requirements and proper acknowledgment. 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." ;
		:cmor_version = "3.15.2" ;
data:

 time = 15.5, 45.5 ;

 time_bnds =
  0, 31,
  31, 60 ;

 plev = 100000, 92500, 85000, 70000, 60000, 50000, 40000, 30000, 25000, 
    20000, 15000, 10000, 7000, 5000, 3000, 2000, 1000, 500, 100 ;

 lat = 10, 20, 30 ;

 lat_bnds =
  5, 15,
  15, 25,
  25, 35 ;

 lon = 0, 90, 180, 270 ;

 lon_bnds =
  -45, 45,
  45, 135,
  135, 225,
  225, 315 ;

 ta =
  _, 250.0549, 250.1099, 250.1648,
  250.2198, 250.2747, 250.3297, 250.3846,
  250.4396, 250.4945, 250.5495, 250.6044,
  250.6593, 250.7143, 250.7692, 250.8242,
  250.8791, 250.9341, 250.989, 251.044,
  251.0989, 251.1538, 251.2088, 251.2637,
  251.3187, 251.3736, 251.4286, 251.4835,
  251.5385, 251.5934, 251.6483, 251.7033,
  251.7582, 251.8132, 251.8681, 251.9231,
  251.978, 252.033, 252.0879, 252.1429,
  252.1978, 252.2527, 252.3077, 252.3626,
  252.4176, 252.4725, 252.5275, 252.5824,
  252.6374, 252.6923, 252.7473, 252.8022,
  252.8571, 252.9121, 252.967, 253.022,
  253.0769, 253.1319, 253.1868, 253.2418,
  253.2967, 253.3517, 253.4066, 253.4615,
  253.5165, 253.5714, 253.6264, 253.6813,
  253.7363, 253.7912, 253.8462, 253.9011,
  253.956, 254.011, 254.0659, 254.1209,
  254.1758, 254.2308, 254.2857, 254.3407,
  254.3956, 254.4505, 254.5055, 254.5604,
  254.6154, 254.6703, 254.7253, 254.7802,
  254.8352, 254.8901, 254.9451, 255,
  255.0549, 255.1099, 255.1648, 255.2198,
  255.2747, 255.3297, 255.3846, 255.4396,
  255.4945, 255.5495, 255.6044, 255.6593,
  255.7143, 255.7692, 255.8242, 255.8791,
  255.9341, 255.989, 256.0439, 256.0989,
  256.1538, 256.2088, 256.2637, 256.3187,
  256.3736, 256.4286, 256.4835, 256.5385,
  256.5934, 256.6483, 256.7033, 256.7582,
  256.8132, 256.8681, 256.9231, 256.978,
  257.033, 257.0879, 257.1429, 257.1978,
  257.2527, 257.3077, 257.3626, 257.4176,
  257.4725, 257.5275, 257.5824, 257.6374,
  257.6923, 257.7473, 257.8022, 257.8571,
  257.9121, 257.967, 258.022, 258.0769,
  258.1319, 258.1868, 258.2418, 258.2967,
  258.3517, 258.4066, 258.4615, 258.5165,
  258.5714, 258.6264, 258.6813, 258.7363,
  258.7912, 258.8462, 258.9011, 258.9561,
  259.011, 259.0659, 259.1209, 259.1758,
  259.2308, 259.2857, 259.3407, 259.3956,
  259.4506, 259.5055, 259.5604, 259.6154,
  259.6703, 259.7253, 259.7802, 259.8352,
  259.8901, 259.9451, 260, 260.0549,
  260.1099, 260.1648, 260.2198, 260.2747,
  260.3297, 260.3846, 260.4396, 260.4945,
  260.5494, 260.6044, 260.6593, 260.7143,
  260.7692, 260.8242, 260.8791, 260.9341,
  260.989, 261.0439, 261.0989, 261.1538,
  261.2088, 261.2637, 261.3187, 261.3736,
  261.4286, 261.4835, 261.5385, 261.5934,
  261.6483, 261.7033, 261.7582, 261.8132,
  261.8681, 261.9231, 261.978, 262.033,
  262.0879, 262.1429, 262.1978, 262.2527,
  262.3077, 262.3626, 262.4176, 262.4725,
  262.5275, 262.5824, 262.6374, 262.6923,
  262.7473, 262.8022, 262.8571, 262.9121,
  262.967, 263.022, 263.0769, 263.1319,
  263.1868, 263.2418, 263.2967, 263.3517,
  263.4066, 263.4615, 263.5165, 263.5714,
  263.6264, 263.6813, 263.7363, 263.7912,
  263.8462, 263.9011, 263.9561, 264.011,
  264.0659, 264.1209, 264.1758, 264.2308,
  264.2857, 264.3407, 264.3956, 264.4506,
  264.5055, 264.5604, 264.6154, 264.6703,
  264.7253, 264.7802, 264.8352, 264.8901,
  264.9451, 265, 265.0549, 265.1099,
  265.1648, 265.2198, 265.2747, 265.3297,
  265.3846, 265.4396, 265.4945, 265.5494,
  265.6044, 265.6593, 265.7143, 265.7692,
  265.8242, 265.8791, 265.9341, 265.989,
  266.0439, 266.0989, 266.1538, 266.2088,
  266.2637, 266.3187, 266.3736, 266.4286,
  266.4835, 266.5385, 266.5934, 266.6483,
  266.7033, 266.7582, 266.8132, 266.8681,
  266.9231, 266.978, 267.033, 267.0879,
  267.1429, 267.1978, 267.2527, 267.3077,
  267.3626, 267.4176, 267.4725, 267.5275,
  267.5824, 267.6374, 267.6923, 267.7473,
  267.8022, 267.8571, 267.9121, 267.967,
  268.022, 268.0769, 268.1319, 268.1868,
  268.2418, 268.2967, 268.3517, 268.4066,
  268.4615, 268.5165, 268.5714, 268.6264,
  268.6813, 268.7363, 268.7912, 268.8462,
  268.9011, 268.9561, 269.011, 269.0659,
  269.1209, 269.1758, 269.2308, 269.2857,
  269.3407, 269.3956, 269.4506, 269.5055,
  269.5604, 269.6154, 269.6703, 269.7253,
  269.7802, 269.8352, 269.8901, 269.9451,
  270, 270.0549, 270.1099, 270.1648,
  270.2198, 270.2747, 270.3297, 270.3846,
  270.4396, 270.4945, 270.5494, 270.6044,
  270.6593, 270.7143, 270.7692, 270.8242,
  270.8791, 270.9341, 270.989, 271.0439,
  271.0989, 271.1538, 271.2088, 271.2637,
  271.3187, 271.3736, 271.4286, 271.4835,
  271.5385, 271.5934, 271.6483, 271.7033,
  271.7582, 271.8132, 271.8681, 271.9231,
  271.978, 272.033, 272.0879, 272.1429,
  272.1978, 272.2527, 272.3077, 272.3626,
  272.4176, 272.4725, 272.5275, 272.5824,
  272.6374, 272.6923, 272.7473, 272.8022,
  272.8571, 272.9121, 272.967, 273.022,
  273.0769, 273.1319, 273.1868, 273.2418,
  273.2967, 273.3517, 273.4066, 273.4615,
  273.5165, 273.5714, 273.6264, 273.6813,
  273.7363, 273.7912, 273.8462, 273.9011,
  273.9561, 274.011, 274.0659, 274.1209,
  274.1758, 274.2308, 274.2857, 274.3407,
  274.3956, 274.4506, 274.5055, 274.5604,
  274.6154, 274.6703, 274.7253, 274.7802,
  274.8352, 274.8901, 274.9451, 275 ;
}


Example 3: Treatment of a Scalar Dimension

Click to expand Python code
#!/usr/bin/env python3

from __future__ import annotations

import argparse
import json
import os
from pathlib import Path

import cmor
import numpy as np

EXAMPLE_DIR = Path(__file__).resolve().parent
RUN_DIR = Path.cwd()
DEFAULT_TABLES_PATH = RUN_DIR / "cmip7-cmor-tables" / "tables"
TABLES_PATH = Path(os.environ.get("CMOR_TABLES_PATH", DEFAULT_TABLES_PATH))
INPUT_PATH = EXAMPLE_DIR.parent / "CMIP7_input_example.json"


def configure(
    output_dir: Path,
    frequency: str = "mon",
    realization_index: str = "r1",
    forcing_index: str = "f1",
) -> None:
    output_dir.mkdir(parents=True, exist_ok=True)
    user_input = json.loads(INPUT_PATH.read_text())
    user_input["outpath"] = str(output_dir)
    user_input["frequency"] = frequency
    user_input["realization_index"] = realization_index
    user_input["forcing_index"] = forcing_index
    input_path = output_dir / "CMIP7_input_example.json"
    input_path.write_text(json.dumps(user_input, indent=2, sort_keys=True))
    cmor.setup(inpath=str(TABLES_PATH), netcdf_file_action=cmor.CMOR_REPLACE)
    cmor.dataset_json(str(input_path))


def apply_cmip7_variable_metadata(
    var_id: int,
    realm: str,
    table_entry: str,
    frequency: str,
    region: str,
) -> str:
    compound_name = ".".join(
        [realm] + table_entry.split("_") + [frequency, region]
    )

    with (TABLES_PATH / "CMIP7_cell_measures.json").open() as handle:
        cell_measures = json.load(handle)["cell_measures"]
    cmor.set_variable_attribute(
        var_id,
        "cell_measures",
        "c",
        cell_measures.get(compound_name, ""),
    )

    with (TABLES_PATH / "CMIP7_long_name_overrides.json").open() as handle:
        long_name_overrides = json.load(handle)["long_name_overrides"]
    if compound_name in long_name_overrides:
        cmor.set_variable_attribute(
            var_id,
            "long_name",
            "c",
            long_name_overrides[compound_name],
        )

    return compound_name


def write_example(output_dir: Path) -> str:
    configure(output_dir, realization_index="r9", forcing_index="f2")
    cmor.load_table("CMIP7_atmos.json")
    lat_id = cmor.axis(
        "latitude",
        "degrees_north",
        coord_vals=np.array([10.0, 20.0, 30.0], dtype="d"),
        cell_bounds=np.array([5.0, 15.0, 25.0, 35.0], dtype="d"),
    )
    lon_id = cmor.axis(
        "longitude",
        "degrees_east",
        coord_vals=np.array([0.0, 90.0, 180.0, 270.0], dtype="d"),
        cell_bounds=np.array([-45.0, 45.0, 135.0, 225.0, 315.0], dtype="d"),
    )
    time_id = cmor.axis(
        "time",
        "days since 1979-01-01",
        coord_vals=np.array([15.5, 45.5], dtype="d"),
        cell_bounds=np.array([0.0, 31.0, 60.0], dtype="d"),
    )
    height_id = cmor.axis(
        "height2m",
        "m",
        coord_vals=np.array([2.0], dtype="d"),
    )
    var_id = cmor.variable(
        "tas_tavg-h2m-hxy-u",
        "K",
        [time_id, lat_id, lon_id, height_id],
        missing_value=1.0e20,
    )
    apply_cmip7_variable_metadata(
        var_id,
        "atmos",
        "tas_tavg-h2m-hxy-u",
        "mon",
        "glb",
    )
    data = np.array(
        [
            254.0895,
            258.4085,
            250.5549,
            258.7101,
            258.6680,
            258.2990,
            252.1237,
            255.0432,
            253.7254,
            251.2460,
            254.3168,
            255.4808,
            259.7908,
            252.2754,
            257.1892,
            253.3132,
            253.8823,
            253.4698,
            253.5381,
            254.9730,
            256.1002,
            251.8168,
            259.3698,
            250.2994,
        ],
        dtype="f4",
    ).reshape(2, 3, 4, 1)
    cmor.write(var_id, data)
    path = cmor.close(var_id, file_name=True)
    cmor.close()
    return path


def main() -> None:
    parser = argparse.ArgumentParser(
        description="Write CMIP7 example 3 with CMOR."
    )
    parser.add_argument(
        "--output-dir",
        type=Path,
        default=Path(__file__).resolve().parent / "output",
    )
    args = parser.parse_args()
    print(write_example(args.output_dir))


if __name__ == "__main__":
    main()

Click to expand NetCDF dump
netcdf tas_tavg-h2m-hxy-u_mon_glb_g999_ACCESS-ESM1-6_amip_r9i1p1f2_197901-197902 {
dimensions:
	time = UNLIMITED ; // (2 currently)
	lat = 3 ;
	lon = 4 ;
	bnds = 2 ;
variables:
	double time(time) ;
		time:bounds = "time_bnds" ;
		time:units = "days since 1979-01-01" ;
		time:calendar = "360_day" ;
		time:axis = "T" ;
		time:long_name = "Time Intervals" ;
		time:standard_name = "time" ;
	double time_bnds(time, bnds) ;
	double lat(lat) ;
		lat:bounds = "lat_bnds" ;
		lat:units = "degrees_north" ;
		lat:axis = "Y" ;
		lat:long_name = "Latitude" ;
		lat:standard_name = "latitude" ;
	double lat_bnds(lat, bnds) ;
	double lon(lon) ;
		lon:bounds = "lon_bnds" ;
		lon:units = "degrees_east" ;
		lon:axis = "X" ;
		lon:long_name = "Longitude" ;
		lon:standard_name = "longitude" ;
	double lon_bnds(lon, bnds) ;
	double height ;
		height:units = "m" ;
		height:axis = "Z" ;
		height:positive = "up" ;
		height:long_name = "height" ;
		height:standard_name = "height" ;
	float tas(time, lat, lon) ;
		tas:standard_name = "air_temperature" ;
		tas:long_name = "Near-Surface Air Temperature" ;
		tas:units = "K" ;
		tas:cell_methods = "area: time: mean" ;
		tas:history = "2026-08-07T00:46:49Z altered by CMOR: Treated scalar dimension: \'height\'." ;
		tas:coordinates = "height" ;
		tas:missing_value = 1.e+20f ;
		tas:_FillValue = 1.e+20f ;
		tas:cell_measures = "area: areacella" ;

// global attributes:
		:Conventions = "CF-1.12" ;
		:activity_id = "CMIP" ;
		:archive_id = "WCRP" ;
		:area_label = "u" ;
		:branded_variable = "tas_tavg-h2m-hxy-u" ;
		:branding_suffix = "tavg-h2m-hxy-u" ;
		:creation_date = "2026-08-07T00:46:49Z" ;
		:data_specs_version = "MIP-DS7.1.0.0" ;
		:description = "Simulation of the climate of the recent past with prescribed sea surface temperatures and sea ice concentrations." ;
		:drs_specs = "MIP-DRS7" ;
		:experiment = "Simulation of the climate of the recent past with prescribed sea surface temperatures and sea ice concentrations." ;
		:experiment_id = "amip" ;
		:external_variables = "areacella" ;
		:forcing_index = "f2" ;
		:frequency = "mon" ;
		:grid_label = "g999" ;
		:history = "2026-08-07T00:46:49Z ; CMOR rewrote data to be consistent with CF-1.12 and CMIP7 data requirements." ;
		:horizontal_label = "hxy" ;
		:host_collection = "CMIP7" ;
		:initialization_index = "i1" ;
		:institution = "Met Office Hadley Centre" ;
		:institution_id = "MOHC" ;
		:license_id = "CC-BY-4.0" ;
		:mip_era = "CMIP7" ;
		:nominal_resolution = "100 km" ;
		:physics_index = "p1" ;
		:product = "model-output" ;
		:realization_index = "r9" ;
		:realm = "atmos" ;
		:region = "glb" ;
		:source = "ACCESS-ESM1-6: aerosol: classic; atmosphere: um7-3; land-surface: cable3; ocean-biogeochemistry: wombatlite; ocean: mom5; sea-ice: cice5" ;
		:source_id = "ACCESS-ESM1-6" ;
		:table_info = "Name: CMIP7_atmos.json; Creation Date:(2026-07-21 13:16:24) MD5:28339aa355908b9331150e1536f5e384" ;
		:temporal_label = "tavg" ;
		:title = "ACCESS-ESM1-6 output prepared for CMIP7" ;
		:tracking_id = "hdl:21.14107/d4e73969-87ad-4f16-abfa-5fc650c54297" ;
		:variable_id = "tas" ;
		:variant_label = "r9i1p1f2" ;
		:vertical_label = "h2m" ;
		:license = "CC-BY-4.0; CMIP7 data produced by MOHC is licensed under a Creative Commons Attribution 4.0 International License (https://creativecommons.org/licenses/by/4.0). Consult https://wcrp-cmip.github.io/cmip7-guidance/docs/CMIP7/Guidance_for_users/#2-terms-of-use-and-citations-requirements for terms of use governing CMIP7 output, including citation requirements and proper acknowledgment. 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." ;
		:cmor_version = "3.15.2" ;
data:

 time = 15.5, 45.5 ;

 time_bnds =
  0, 31,
  31, 60 ;

 lat = 10, 20, 30 ;

 lat_bnds =
  5, 15,
  15, 25,
  25, 35 ;

 lon = 0, 90, 180, 270 ;

 lon_bnds =
  -45, 45,
  45, 135,
  135, 225,
  225, 315 ;

 height = 2 ;

 tas =
  254.0895, 258.4085, 250.5549, 258.7101,
  258.668, 258.299, 252.1237, 255.0432,
  253.7254, 251.246, 254.3168, 255.4808,
  259.7908, 252.2754, 257.1892, 253.3132,
  253.8823, 253.4698, 253.5381, 254.973,
  256.1002, 251.8168, 259.3698, 250.2994 ;
}


Example 4: Treatment of Auxiliary Coordinates

Click to expand Python code
#!/usr/bin/env python3

from __future__ import annotations

import argparse
import json
import os
from pathlib import Path

import cmor
import numpy as np

EXAMPLE_DIR = Path(__file__).resolve().parent
RUN_DIR = Path.cwd()
DEFAULT_TABLES_PATH = RUN_DIR / "cmip7-cmor-tables" / "tables"
TABLES_PATH = Path(os.environ.get("CMOR_TABLES_PATH", DEFAULT_TABLES_PATH))
INPUT_PATH = EXAMPLE_DIR.parent / "CMIP7_input_example.json"


def configure(
    output_dir: Path,
    frequency: str = "mon",
    realization_index: str = "r1",
    forcing_index: str = "f1",
) -> None:
    output_dir.mkdir(parents=True, exist_ok=True)
    user_input = json.loads(INPUT_PATH.read_text())
    user_input["outpath"] = str(output_dir)
    user_input["frequency"] = frequency
    user_input["realization_index"] = realization_index
    user_input["forcing_index"] = forcing_index
    input_path = output_dir / "CMIP7_input_example.json"
    input_path.write_text(json.dumps(user_input, indent=2, sort_keys=True))
    cmor.setup(inpath=str(TABLES_PATH), netcdf_file_action=cmor.CMOR_REPLACE)
    cmor.dataset_json(str(input_path))


def apply_cmip7_variable_metadata(
    var_id: int,
    realm: str,
    table_entry: str,
    frequency: str,
    region: str,
) -> str:
    compound_name = ".".join(
        [realm] + table_entry.split("_") + [frequency, region]
    )

    with (TABLES_PATH / "CMIP7_cell_measures.json").open() as handle:
        cell_measures = json.load(handle)["cell_measures"]
    cmor.set_variable_attribute(
        var_id,
        "cell_measures",
        "c",
        cell_measures.get(compound_name, ""),
    )

    with (TABLES_PATH / "CMIP7_long_name_overrides.json").open() as handle:
        long_name_overrides = json.load(handle)["long_name_overrides"]
    if compound_name in long_name_overrides:
        cmor.set_variable_attribute(
            var_id,
            "long_name",
            "c",
            long_name_overrides[compound_name],
        )

    return compound_name


def write_example(output_dir: Path) -> str:
    configure(output_dir)
    cmor.load_table("CMIP7_ocean.json")
    time_id = cmor.axis(
        "time",
        "days since 1979-01-01",
        coord_vals=np.array([15.5, 45.5], dtype="d"),
        cell_bounds=np.array([0.0, 31.0, 60.0], dtype="d"),
    )
    lat_id = cmor.axis(
        "latitude",
        "degrees_north",
        coord_vals=np.array([10.0, 20.0, 30.0], dtype="d"),
        cell_bounds=np.array([5.0, 15.0, 25.0, 35.0], dtype="d"),
    )
    basin_id = cmor.axis(
        "basin",
        "",
        coord_vals=np.array(
            [
                "atlantic_arctic_ocean",
                "indian_pacific_ocean",
                "global_ocean",
            ],
            dtype="U21",
        ),
    )
    var_id = cmor.variable(
        "htovgyre_tavg-u-hyb-sea",
        "W",
        [time_id, basin_id, lat_id],
        missing_value=1.0e20,
    )
    apply_cmip7_variable_metadata(
        var_id,
        "ocean",
        "htovgyre_tavg-u-hyb-sea",
        "mon",
        "glb",
    )
    data = np.array(
        [
            -80.0,
            -84.0,
            -88.0,
            -100.0,
            -104.0,
            -76.0,
            -120.0,
            -92.0,
            -96.0,
            -79.0,
            -83.0,
            -87.0,
            -99.0,
            -103.0,
            -75.0,
            -107.0,
            -111.0,
            -115.0,
        ],
        dtype="f4",
    ).reshape(2, 3, 3)
    cmor.write(var_id, data)
    path = cmor.close(var_id, file_name=True)
    cmor.close()
    return path


def main() -> None:
    parser = argparse.ArgumentParser(
        description="Write CMIP7 example 4 with CMOR."
    )
    parser.add_argument(
        "--output-dir",
        type=Path,
        default=Path(__file__).resolve().parent / "output",
    )
    args = parser.parse_args()
    print(write_example(args.output_dir))


if __name__ == "__main__":
    main()

Click to expand NetCDF dump
netcdf htovgyre_tavg-u-hyb-sea_mon_glb_g999_ACCESS-ESM1-6_amip_r1i1p1f1_197901-197902 {
dimensions:
	time = UNLIMITED ; // (2 currently)
	basin = 3 ;
	lat = 3 ;
	bnds = 2 ;
	strlen = 21 ;
variables:
	double time(time) ;
		time:bounds = "time_bnds" ;
		time:units = "days since 1979-01-01" ;
		time:calendar = "360_day" ;
		time:axis = "T" ;
		time:long_name = "Time Intervals" ;
		time:standard_name = "time" ;
	double time_bnds(time, bnds) ;
	char sector(basin, strlen) ;
		sector:long_name = "Ocean Basin" ;
		sector:standard_name = "region" ;
	double lat(lat) ;
		lat:bounds = "lat_bnds" ;
		lat:units = "degrees_north" ;
		lat:axis = "Y" ;
		lat:long_name = "Latitude" ;
		lat:standard_name = "latitude" ;
	double lat_bnds(lat, bnds) ;
	float htovgyre(time, basin, lat) ;
		htovgyre:standard_name = "northward_ocean_heat_transport_due_to_gyre" ;
		htovgyre:long_name = "Northward Ocean Heat Transport Due to Gyre" ;
		htovgyre:units = "W" ;
		htovgyre:cell_methods = "depth: longitude: sum where sea (along a zig-zag grid path spanning a basin)  time: mean" ;
		htovgyre:missing_value = 1.e+20f ;
		htovgyre:_FillValue = 1.e+20f ;
		htovgyre:coordinates = "sector" ;

// global attributes:
		:Conventions = "CF-1.12" ;
		:activity_id = "CMIP" ;
		:archive_id = "WCRP" ;
		:area_label = "sea" ;
		:branded_variable = "htovgyre_tavg-u-hyb-sea" ;
		:branding_suffix = "tavg-u-hyb-sea" ;
		:creation_date = "2026-08-07T00:46:50Z" ;
		:data_specs_version = "MIP-DS7.1.0.0" ;
		:description = "Simulation of the climate of the recent past with prescribed sea surface temperatures and sea ice concentrations." ;
		:drs_specs = "MIP-DRS7" ;
		:experiment = "Simulation of the climate of the recent past with prescribed sea surface temperatures and sea ice concentrations." ;
		:experiment_id = "amip" ;
		:forcing_index = "f1" ;
		:frequency = "mon" ;
		:grid_label = "g999" ;
		:history = "2026-08-07T00:46:50Z ; CMOR rewrote data to be consistent with CF-1.12 and CMIP7 data requirements." ;
		:horizontal_label = "hyb" ;
		:host_collection = "CMIP7" ;
		:initialization_index = "i1" ;
		:institution = "Met Office Hadley Centre" ;
		:institution_id = "MOHC" ;
		:license_id = "CC-BY-4.0" ;
		:mip_era = "CMIP7" ;
		:nominal_resolution = "100 km" ;
		:physics_index = "p1" ;
		:product = "model-output" ;
		:realization_index = "r1" ;
		:realm = "ocean" ;
		:region = "glb" ;
		:source = "ACCESS-ESM1-6: aerosol: classic; atmosphere: um7-3; land-surface: cable3; ocean-biogeochemistry: wombatlite; ocean: mom5; sea-ice: cice5" ;
		:source_id = "ACCESS-ESM1-6" ;
		:table_info = "Name: CMIP7_ocean.json; Creation Date:(2026-07-21 13:16:24) MD5:734c6f53560fa60b7c1c21b38e5b9a3f" ;
		:temporal_label = "tavg" ;
		:title = "ACCESS-ESM1-6 output prepared for CMIP7" ;
		:tracking_id = "hdl:21.14107/29d2ba90-14ef-4e3d-9b5f-3d171d3eb35d" ;
		:variable_id = "htovgyre" ;
		:variant_label = "r1i1p1f1" ;
		:vertical_label = "u" ;
		:license = "CC-BY-4.0; CMIP7 data produced by MOHC is licensed under a Creative Commons Attribution 4.0 International License (https://creativecommons.org/licenses/by/4.0). Consult https://wcrp-cmip.github.io/cmip7-guidance/docs/CMIP7/Guidance_for_users/#2-terms-of-use-and-citations-requirements for terms of use governing CMIP7 output, including citation requirements and proper acknowledgment. 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." ;
		:cmor_version = "3.15.2" ;
data:

 time = 15.5, 45.5 ;

 time_bnds =
  0, 31,
  31, 60 ;

 sector =
  "atlantic_arctic_ocean",
  "indian_pacific_ocean",
  "global_ocean" ;

 lat = 10, 20, 30 ;

 lat_bnds =
  5, 15,
  15, 25,
  25, 35 ;

 htovgyre =
  -80, -84, -88,
  -100, -104, -76,
  -120, -92, -96,
  -79, -83, -87,
  -99, -103, -75,
  -107, -111, -115 ;
}


Example 5: Treatment of a 3-D Field on Model Levels

Click to expand Python code
#!/usr/bin/env python3

from __future__ import annotations

import argparse
import json
import os
from pathlib import Path

import cmor
import numpy as np

EXAMPLE_DIR = Path(__file__).resolve().parent
RUN_DIR = Path.cwd()
DEFAULT_TABLES_PATH = RUN_DIR / "cmip7-cmor-tables" / "tables"
TABLES_PATH = Path(os.environ.get("CMOR_TABLES_PATH", DEFAULT_TABLES_PATH))
INPUT_PATH = EXAMPLE_DIR.parent / "CMIP7_input_example.json"


def configure(
    output_dir: Path,
    frequency: str = "mon",
    realization_index: str = "r1",
    forcing_index: str = "f1",
) -> None:
    output_dir.mkdir(parents=True, exist_ok=True)
    user_input = json.loads(INPUT_PATH.read_text())
    user_input["outpath"] = str(output_dir)
    user_input["frequency"] = frequency
    user_input["realization_index"] = realization_index
    user_input["forcing_index"] = forcing_index
    input_path = output_dir / "CMIP7_input_example.json"
    input_path.write_text(json.dumps(user_input, indent=2, sort_keys=True))
    cmor.setup(inpath=str(TABLES_PATH), netcdf_file_action=cmor.CMOR_REPLACE)
    cmor.dataset_json(str(input_path))


def apply_cmip7_variable_metadata(
    var_id: int,
    realm: str,
    table_entry: str,
    frequency: str,
    region: str,
) -> str:
    compound_name = ".".join(
        [realm] + table_entry.split("_") + [frequency, region]
    )

    with (TABLES_PATH / "CMIP7_cell_measures.json").open() as handle:
        cell_measures = json.load(handle)["cell_measures"]
    cmor.set_variable_attribute(
        var_id,
        "cell_measures",
        "c",
        cell_measures.get(compound_name, ""),
    )

    with (TABLES_PATH / "CMIP7_long_name_overrides.json").open() as handle:
        long_name_overrides = json.load(handle)["long_name_overrides"]
    if compound_name in long_name_overrides:
        cmor.set_variable_attribute(
            var_id,
            "long_name",
            "c",
            long_name_overrides[compound_name],
        )

    return compound_name


def write_example(output_dir: Path) -> str:
    configure(output_dir)
    cmor.load_table("CMIP7_atmos.json")
    lat_id = cmor.axis(
        "latitude",
        "degrees_north",
        coord_vals=np.array([10.0, 20.0, 30.0], dtype="d"),
        cell_bounds=np.array([5.0, 15.0, 25.0, 35.0], dtype="d"),
    )
    lon_id = cmor.axis(
        "longitude",
        "degrees_east",
        coord_vals=np.array([0.0, 90.0, 180.0, 270.0], dtype="d"),
        cell_bounds=np.array([-45.0, 45.0, 135.0, 225.0, 315.0], dtype="d"),
    )
    time_id = cmor.axis(
        "time",
        "days since 1979-01-01",
        coord_vals=np.array([15.0, 45.0], dtype="d"),
        cell_bounds=np.array([0.0, 30.0, 60.0], dtype="d"),
    )
    lev_id = cmor.axis(
        "standard_hybrid_sigma",
        "1",
        coord_vals=np.array([0.92, 0.72, 0.50, 0.30, 0.10], dtype="d"),
        cell_bounds=np.array([1.00, 0.83, 0.61, 0.40, 0.20, 0.00], dtype="d"),
    )
    cmor.zfactor(
        zaxis_id=lev_id,
        zfactor_name="a",
        axis_ids=[lev_id],
        zfactor_values=np.array([0.12, 0.22, 0.30, 0.20, 0.10], dtype="d"),
        zfactor_bounds=np.array(
            [0.06, 0.18, 0.26, 0.25, 0.15, 0.00],
            dtype="d",
        ),
    )
    cmor.zfactor(
        zaxis_id=lev_id,
        zfactor_name="b",
        axis_ids=[lev_id],
        zfactor_values=np.array([0.80, 0.50, 0.20, 0.10, 0.00], dtype="d"),
        zfactor_bounds=np.array(
            [0.94, 0.65, 0.35, 0.15, 0.05, 0.00],
            dtype="d",
        ),
    )
    cmor.zfactor(
        zaxis_id=lev_id,
        zfactor_name="p0",
        units="Pa",
        zfactor_values=100000.0,
    )
    ps = np.array(
        [
            97000.0,
            97400.0,
            97800.0,
            98200.0,
            98600.0,
            99000.0,
            99400.0,
            99800.0,
            100200.0,
            100600.0,
            101000.0,
            101400.0,
            97100.0,
            97500.0,
            97900.0,
            98300.0,
            98700.0,
            99100.0,
            99500.0,
            99900.0,
            100300.0,
            100700.0,
            101100.0,
            101500.0,
        ],
        dtype="f4",
    ).reshape(2, 3, 4)
    ps_id = cmor.zfactor(
        zaxis_id=lev_id,
        zfactor_name="ps",
        axis_ids=[time_id, lat_id, lon_id],
        units="Pa",
    )
    var_id = cmor.variable(
        "cl_tavg-al-hxy-u",
        "%",
        [time_id, lev_id, lat_id, lon_id],
        missing_value=1.0e20,
    )
    apply_cmip7_variable_metadata(
        var_id,
        "atmos",
        "cl_tavg-al-hxy-u",
        "mon",
        "glb",
    )
    data = np.array(
        [
            72.8, 73.2, 73.6, 74.0,
            71.6, 72.0, 72.4, 72.4,
            70.4, 70.8, 70.8, 71.2,
            67.6, 69.2, 69.6, 70.0,
            66.0, 66.4, 66.8, 67.2,
            64.8, 65.2, 65.6, 66.0,
            63.6, 64.0, 64.4, 64.4,
            60.8, 61.2, 62.8, 63.2,
            59.6, 59.6, 60.0, 60.4,
            58.0, 58.4, 58.8, 59.2,
            56.8, 57.2, 57.6, 58.0,
            54.0, 54.4, 54.8, 56.4,
            52.8, 53.2, 53.2, 53.6,
            51.6, 51.6, 52.0, 52.4,
            50.0, 50.4, 50.8, 51.2,
            72.9, 73.3, 73.7, 74.1,
            71.7, 72.1, 72.5, 72.5,
            70.5, 70.9, 70.9, 71.3,
            67.7, 69.3, 69.7, 70.1,
            66.1, 66.5, 66.9, 67.3,
            64.9, 65.3, 65.7, 66.1,
            63.7, 64.1, 64.5, 64.5,
            60.9, 61.3, 62.9, 63.3,
            59.7, 59.7, 60.1, 60.5,
            58.1, 58.5, 58.9, 59.3,
            56.9, 57.3, 57.7, 58.1,
            54.1, 54.5, 54.9, 56.5,
            52.9, 53.3, 53.3, 53.7,
            51.7, 51.7, 52.1, 52.5,
            50.1, 50.5, 50.9, 51.3,
        ],
        dtype="f4",
    ).reshape(2, 5, 3, 4)
    cmor.write(var_id, data)
    cmor.write(ps_id, ps, store_with=var_id)
    path = cmor.close(var_id, file_name=True)
    cmor.close()
    return path


def main() -> None:
    parser = argparse.ArgumentParser(
        description="Write CMIP7 example 5 with CMOR."
    )
    parser.add_argument(
        "--output-dir",
        type=Path,
        default=Path(__file__).resolve().parent / "output",
    )
    args = parser.parse_args()
    print(write_example(args.output_dir))


if __name__ == "__main__":
    main()

Click to expand NetCDF dump
netcdf cl_tavg-al-hxy-u_mon_glb_g999_ACCESS-ESM1-6_amip_r1i1p1f1_197901-197902 {
dimensions:
	time = UNLIMITED ; // (2 currently)
	lev = 5 ;
	lat = 3 ;
	lon = 4 ;
	bnds = 2 ;
variables:
	double time(time) ;
		time:bounds = "time_bnds" ;
		time:units = "days since 1979-01-01" ;
		time:calendar = "360_day" ;
		time:axis = "T" ;
		time:long_name = "Time Intervals" ;
		time:standard_name = "time" ;
	double time_bnds(time, bnds) ;
	double lev(lev) ;
		lev:bounds = "lev_bnds" ;
		lev:units = "1" ;
		lev:axis = "Z" ;
		lev:positive = "down" ;
		lev:long_name = "hybrid sigma pressure coordinate" ;
		lev:standard_name = "atmosphere_hybrid_sigma_pressure_coordinate" ;
		lev:formula = "p = a*p0 + b*ps" ;
		lev:formula_terms = "p0: p0 a: a b: b ps: ps" ;
	double lev_bnds(lev, bnds) ;
		lev_bnds:formula = "p = a*p0 + b*ps" ;
		lev_bnds:standard_name = "atmosphere_hybrid_sigma_pressure_coordinate" ;
		lev_bnds:units = "1" ;
		lev_bnds:formula_terms = "p0: p0 a: a_bnds b: b_bnds ps: ps" ;
	double p0 ;
		p0:standard_name = "reference_air_pressure_for_atmosphere_vertical_coordinate" ;
		p0:long_name = "vertical coordinate formula term: reference pressure" ;
		p0:units = "Pa" ;
	double a(lev) ;
		a:long_name = "vertical coordinate formula term: a" ;
	double b(lev) ;
		b:long_name = "vertical coordinate formula term: b" ;
	float ps(time, lat, lon) ;
		ps:standard_name = "air_pressure" ;
		ps:long_name = "Surface Air Pressure" ;
		ps:units = "Pa" ;
	double a_bnds(lev, bnds) ;
		a_bnds:long_name = "vertical coordinate formula term: a(k+1/2)" ;
	double b_bnds(lev, bnds) ;
		b_bnds:long_name = "vertical coordinate formula term: b(k+1/2)" ;
	double lat(lat) ;
		lat:bounds = "lat_bnds" ;
		lat:units = "degrees_north" ;
		lat:axis = "Y" ;
		lat:long_name = "Latitude" ;
		lat:standard_name = "latitude" ;
	double lat_bnds(lat, bnds) ;
	double lon(lon) ;
		lon:bounds = "lon_bnds" ;
		lon:units = "degrees_east" ;
		lon:axis = "X" ;
		lon:long_name = "Longitude" ;
		lon:standard_name = "longitude" ;
	double lon_bnds(lon, bnds) ;
	float cl(time, lev, lat, lon) ;
		cl:standard_name = "cloud_area_fraction_in_atmosphere_layer" ;
		cl:long_name = "Percentage Cloud Cover" ;
		cl:units = "%" ;
		cl:cell_methods = "area: time: mean" ;
		cl:missing_value = 1.e+20f ;
		cl:_FillValue = 1.e+20f ;
		cl:cell_measures = "area: areacella" ;

// global attributes:
		:Conventions = "CF-1.12" ;
		:activity_id = "CMIP" ;
		:archive_id = "WCRP" ;
		:area_label = "u" ;
		:branded_variable = "cl_tavg-al-hxy-u" ;
		:branding_suffix = "tavg-al-hxy-u" ;
		:creation_date = "2026-08-07T00:46:51Z" ;
		:data_specs_version = "MIP-DS7.1.0.0" ;
		:description = "Simulation of the climate of the recent past with prescribed sea surface temperatures and sea ice concentrations." ;
		:drs_specs = "MIP-DRS7" ;
		:experiment = "Simulation of the climate of the recent past with prescribed sea surface temperatures and sea ice concentrations." ;
		:experiment_id = "amip" ;
		:external_variables = "areacella" ;
		:forcing_index = "f1" ;
		:frequency = "mon" ;
		:grid_label = "g999" ;
		:history = "2026-08-07T00:46:51Z ; CMOR rewrote data to be consistent with CF-1.12 and CMIP7 data requirements." ;
		:horizontal_label = "hxy" ;
		:host_collection = "CMIP7" ;
		:initialization_index = "i1" ;
		:institution = "Met Office Hadley Centre" ;
		:institution_id = "MOHC" ;
		:license_id = "CC-BY-4.0" ;
		:mip_era = "CMIP7" ;
		:nominal_resolution = "100 km" ;
		:physics_index = "p1" ;
		:product = "model-output" ;
		:realization_index = "r1" ;
		:realm = "atmos" ;
		:region = "glb" ;
		:source = "ACCESS-ESM1-6: aerosol: classic; atmosphere: um7-3; land-surface: cable3; ocean-biogeochemistry: wombatlite; ocean: mom5; sea-ice: cice5" ;
		:source_id = "ACCESS-ESM1-6" ;
		:table_info = "Name: CMIP7_atmos.json; Creation Date:(2026-07-21 13:16:24) MD5:28339aa355908b9331150e1536f5e384" ;
		:temporal_label = "tavg" ;
		:title = "ACCESS-ESM1-6 output prepared for CMIP7" ;
		:variable_id = "cl" ;
		:variant_label = "r1i1p1f1" ;
		:vertical_label = "al" ;
		:license = "CC-BY-4.0; CMIP7 data produced by MOHC is licensed under a Creative Commons Attribution 4.0 International License (https://creativecommons.org/licenses/by/4.0). Consult https://wcrp-cmip.github.io/cmip7-guidance/docs/CMIP7/Guidance_for_users/#2-terms-of-use-and-citations-requirements for terms of use governing CMIP7 output, including citation requirements and proper acknowledgment. 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." ;
		:cmor_version = "3.15.2" ;
		:tracking_id = "hdl:21.14107/17a3a87a-88ac-46fd-82f6-c55c756211d5" ;
data:

 time = 15, 45 ;

 time_bnds =
  0, 30,
  30, 60 ;

 lev = 0.92, 0.72, 0.5, 0.3, 0.1 ;

 lev_bnds =
  1, 0.83,
  0.83, 0.61,
  0.61, 0.4,
  0.4, 0.2,
  0.2, 0 ;

 p0 = 100000 ;

 a = 0.12, 0.22, 0.3, 0.2, 0.1 ;

 b = 0.8, 0.5, 0.2, 0.1, 0 ;

 ps =
  97000, 97400, 97800, 98200,
  98600, 99000, 99400, 99800,
  100200, 100600, 101000, 101400,
  97100, 97500, 97900, 98300,
  98700, 99100, 99500, 99900,
  100300, 100700, 101100, 101500 ;

 a_bnds =
  0.06, 0.18,
  0.18, 0.26,
  0.26, 0.25,
  0.25, 0.15,
  0.15, 0 ;

 b_bnds =
  0.94, 0.65,
  0.65, 0.35,
  0.35, 0.15,
  0.15, 0.05,
  0.05, 0 ;

 lat = 10, 20, 30 ;

 lat_bnds =
  5, 15,
  15, 25,
  25, 35 ;

 lon = 0, 90, 180, 270 ;

 lon_bnds =
  -45, 45,
  45, 135,
  135, 225,
  225, 315 ;

 cl =
  72.8, 73.2, 73.6, 74,
  71.6, 72, 72.4, 72.4,
  70.4, 70.8, 70.8, 71.2,
  67.6, 69.2, 69.6, 70,
  66, 66.4, 66.8, 67.2,
  64.8, 65.2, 65.6, 66,
  63.6, 64, 64.4, 64.4,
  60.8, 61.2, 62.8, 63.2,
  59.6, 59.6, 60, 60.4,
  58, 58.4, 58.8, 59.2,
  56.8, 57.2, 57.6, 58,
  54, 54.4, 54.8, 56.4,
  52.8, 53.2, 53.2, 53.6,
  51.6, 51.6, 52, 52.4,
  50, 50.4, 50.8, 51.2,
  72.9, 73.3, 73.7, 74.1,
  71.7, 72.1, 72.5, 72.5,
  70.5, 70.9, 70.9, 71.3,
  67.7, 69.3, 69.7, 70.1,
  66.1, 66.5, 66.9, 67.3,
  64.9, 65.3, 65.7, 66.1,
  63.7, 64.1, 64.5, 64.5,
  60.9, 61.3, 62.9, 63.3,
  59.7, 59.7, 60.1, 60.5,
  58.1, 58.5, 58.9, 59.3,
  56.9, 57.3, 57.7, 58.1,
  54.1, 54.5, 54.9, 56.5,
  52.9, 53.3, 53.3, 53.7,
  51.7, 51.7, 52.1, 52.5,
  50.1, 50.5, 50.9, 51.3 ;
}


Example 6: Treatment of Grid Coordinates

Click to expand Python code
#!/usr/bin/env python3

from __future__ import annotations

import argparse
import json
import os
from pathlib import Path

import cmor
import numpy as np

EXAMPLE_DIR = Path(__file__).resolve().parent
RUN_DIR = Path.cwd()
DEFAULT_TABLES_PATH = RUN_DIR / "cmip7-cmor-tables" / "tables"
TABLES_PATH = Path(os.environ.get("CMOR_TABLES_PATH", DEFAULT_TABLES_PATH))
INPUT_PATH = EXAMPLE_DIR.parent / "CMIP7_input_example.json"


def configure(
    output_dir: Path,
    frequency: str = "mon",
    realization_index: str = "r1",
    forcing_index: str = "f1",
) -> None:
    output_dir.mkdir(parents=True, exist_ok=True)
    user_input = json.loads(INPUT_PATH.read_text())
    user_input["outpath"] = str(output_dir)
    user_input["frequency"] = frequency
    user_input["realization_index"] = realization_index
    user_input["forcing_index"] = forcing_index
    input_path = output_dir / "CMIP7_input_example.json"
    input_path.write_text(json.dumps(user_input, indent=2, sort_keys=True))
    cmor.setup(inpath=str(TABLES_PATH), netcdf_file_action=cmor.CMOR_REPLACE)
    cmor.dataset_json(str(input_path))


def apply_cmip7_variable_metadata(
    var_id: int,
    realm: str,
    table_entry: str,
    frequency: str,
    region: str,
) -> str:
    compound_name = ".".join(
        [realm] + table_entry.split("_") + [frequency, region]
    )

    with (TABLES_PATH / "CMIP7_cell_measures.json").open() as handle:
        cell_measures = json.load(handle)["cell_measures"]
    cmor.set_variable_attribute(
        var_id,
        "cell_measures",
        "c",
        cell_measures.get(compound_name, ""),
    )

    with (TABLES_PATH / "CMIP7_long_name_overrides.json").open() as handle:
        long_name_overrides = json.load(handle)["long_name_overrides"]
    if compound_name in long_name_overrides:
        cmor.set_variable_attribute(
            var_id,
            "long_name",
            "c",
            long_name_overrides[compound_name],
        )

    return compound_name


def write_example(output_dir: Path) -> str:
    configure(output_dir)
    grid_table = cmor.load_table("CMIP7_grids.json")
    cmor.set_table(grid_table)
    y_id = cmor.axis(
        "y",
        "m",
        coord_vals=np.array([0.0, 10000.0, 20000.0], dtype="d"),
        cell_bounds=np.array(
            [-5000.0, 5000.0, 15000.0, 25000.0],
            dtype="d",
        ),
    )
    x_id = cmor.axis(
        "x",
        "m",
        coord_vals=np.array([0.0, 10000.0, 20000.0, 30000.0], dtype="d"),
        cell_bounds=np.array(
            [-5000.0, 5000.0, 15000.0, 25000.0, 35000.0],
            dtype="d",
        ),
    )
    latitude = np.array(
        [
            [10.0, 8.0, 6.0, 4.0],
            [20.0, 18.0, 16.0, 14.0],
            [30.0, 28.0, 26.0, 24.0],
        ],
        dtype="d",
    )
    longitude = np.array(
        [
            [280.0, 290.0, 300.0, 310.0],
            [282.0, 292.0, 302.0, 312.0],
            [284.0, 294.0, 304.0, 314.0],
        ],
        dtype="d",
    )
    latitude_vertices = np.empty((3, 4, 4), dtype="d")
    longitude_vertices = np.empty((3, 4, 4), dtype="d")
    for j in range(3):
        for i in range(4):
            latitude_vertices[j, i, :] = [
                latitude[j, i] - 5.0,
                latitude[j, i] - 4.0,
                latitude[j, i] + 5.0,
                latitude[j, i] + 4.0,
            ]
            longitude_vertices[j, i, :] = [
                longitude[j, i] - 5.0,
                longitude[j, i] + 5.0,
                longitude[j, i] + 5.0,
                longitude[j, i] - 5.0,
            ]
    grid_id = cmor.grid(
        axis_ids=[y_id, x_id],
        latitude=latitude,
        longitude=longitude,
        latitude_vertices=latitude_vertices,
        longitude_vertices=longitude_vertices,
    )
    cmor.set_grid_mapping(
        grid_id=grid_id,
        mapping_name="lambert_conformal_conic",
        parameter_names=[
            "standard_parallel1",
            "longitude_of_central_meridian",
            "latitude_of_projection_origin",
            "false_easting",
            "false_northing",
            "standard_parallel2",
        ],
        parameter_values=[-20.0, 175.0, 13.0, 8.0, 0.0, 20.0],
        parameter_units=["", "", "", "", "", ""],
    )
    cmor.load_table("CMIP7_atmos.json")
    time_id = cmor.axis(
        "time",
        "days since 1979-01-01",
        coord_vals=np.array([15.5, 45.5], dtype="d"),
        cell_bounds=np.array([0.0, 31.0, 60.0], dtype="d"),
    )
    var_id = cmor.variable(
        "hfls_tavg-u-hxy-u",
        "W m-2",
        [time_id, grid_id],
        positive="up",
        missing_value=1.0e20,
    )
    apply_cmip7_variable_metadata(
        var_id,
        "atmos",
        "hfls_tavg-u-hxy-u",
        "mon",
        "glb",
    )
    data = np.array(
        [
            80.0,
            82.0,
            84.0,
            86.0,
            88.0,
            90.0,
            92.0,
            94.0,
            96.0,
            98.0,
            100.0,
            102.0,
            81.0,
            83.0,
            85.0,
            87.0,
            89.0,
            91.0,
            93.0,
            95.0,
            97.0,
            99.0,
            101.0,
            103.0,
        ],
        dtype="f4",
    ).reshape(2, 3, 4)
    cmor.write(var_id, data)
    path = cmor.close(var_id, file_name=True)
    cmor.close()
    return path


def main() -> None:
    parser = argparse.ArgumentParser(
        description="Write CMIP7 example 6 with CMOR."
    )
    parser.add_argument(
        "--output-dir",
        type=Path,
        default=Path(__file__).resolve().parent / "output",
    )
    args = parser.parse_args()
    print(write_example(args.output_dir))


if __name__ == "__main__":
    main()

Click to expand NetCDF dump
netcdf hfls_tavg-u-hxy-u_mon_glb_g999_ACCESS-ESM1-6_amip_r1i1p1f1_197901-197902 {
dimensions:
	time = UNLIMITED ; // (2 currently)
	x = 4 ;
	y = 3 ;
	bnds = 2 ;
	vertices = 4 ;
variables:
	double time(time) ;
		time:bounds = "time_bnds" ;
		time:units = "days since 1979-01-01" ;
		time:calendar = "360_day" ;
		time:axis = "T" ;
		time:long_name = "Time Intervals" ;
		time:standard_name = "time" ;
	double time_bnds(time, bnds) ;
	double x(x) ;
		x:bounds = "x_bnds" ;
		x:units = "m" ;
		x:axis = "X" ;
		x:long_name = "x coordinate of projection" ;
		x:standard_name = "projection_x_coordinate" ;
	double x_bnds(x, bnds) ;
	double y(y) ;
		y:bounds = "y_bnds" ;
		y:units = "m" ;
		y:axis = "Y" ;
		y:long_name = "y coordinate of projection" ;
		y:standard_name = "projection_y_coordinate" ;
	double y_bnds(y, bnds) ;
	int lambert_conformal_conic ;
		lambert_conformal_conic:grid_mapping_name = "lambert_conformal_conic" ;
		lambert_conformal_conic:standard_parallel = -20., 20. ;
		lambert_conformal_conic:longitude_of_central_meridian = 175. ;
		lambert_conformal_conic:latitude_of_projection_origin = 13. ;
		lambert_conformal_conic:false_easting = 8. ;
		lambert_conformal_conic:false_northing = 0. ;
	double latitude(x, y) ;
		latitude:standard_name = "latitude" ;
		latitude:long_name = "latitude" ;
		latitude:units = "degrees_north" ;
		latitude:missing_value = 1.e+20 ;
		latitude:_FillValue = 1.e+20 ;
		latitude:bounds = "vertices_latitude" ;
	double longitude(x, y) ;
		longitude:standard_name = "longitude" ;
		longitude:long_name = "longitude" ;
		longitude:units = "degrees_east" ;
		longitude:missing_value = 1.e+20 ;
		longitude:_FillValue = 1.e+20 ;
		longitude:bounds = "vertices_longitude" ;
	double vertices_latitude(x, y, vertices) ;
		vertices_latitude:units = "degrees_north" ;
		vertices_latitude:missing_value = 1.e+20 ;
		vertices_latitude:_FillValue = 1.e+20 ;
	double vertices_longitude(x, y, vertices) ;
		vertices_longitude:units = "degrees_east" ;
		vertices_longitude:missing_value = 1.e+20 ;
		vertices_longitude:_FillValue = 1.e+20 ;
	float hfls(time, x, y) ;
		hfls:standard_name = "surface_upward_latent_heat_flux" ;
		hfls:long_name = "Surface Upward Latent Heat Flux" ;
		hfls:units = "W m-2" ;
		hfls:cell_methods = "area: time: mean" ;
		hfls:history = "2026-08-07T00:46:52Z altered by CMOR: Reordered dimensions, original order: time y x." ;
		hfls:missing_value = 1.e+20f ;
		hfls:_FillValue = 1.e+20f ;
		hfls:cell_measures = "area: areacella" ;
		hfls:grid_mapping = "lambert_conformal_conic" ;
		hfls:coordinates = "latitude longitude" ;

// global attributes:
		:Conventions = "CF-1.12" ;
		:activity_id = "CMIP" ;
		:archive_id = "WCRP" ;
		:area_label = "u" ;
		:branded_variable = "hfls_tavg-u-hxy-u" ;
		:branding_suffix = "tavg-u-hxy-u" ;
		:creation_date = "2026-08-07T00:46:52Z" ;
		:data_specs_version = "MIP-DS7.1.0.0" ;
		:description = "Simulation of the climate of the recent past with prescribed sea surface temperatures and sea ice concentrations." ;
		:drs_specs = "MIP-DRS7" ;
		:experiment = "Simulation of the climate of the recent past with prescribed sea surface temperatures and sea ice concentrations." ;
		:experiment_id = "amip" ;
		:external_variables = "areacella" ;
		:forcing_index = "f1" ;
		:frequency = "mon" ;
		:grid_label = "g999" ;
		:history = "2026-08-07T00:46:52Z ; CMOR rewrote data to be consistent with CF-1.12 and CMIP7 data requirements." ;
		:horizontal_label = "hxy" ;
		:host_collection = "CMIP7" ;
		:initialization_index = "i1" ;
		:institution = "Met Office Hadley Centre" ;
		:institution_id = "MOHC" ;
		:license_id = "CC-BY-4.0" ;
		:mip_era = "CMIP7" ;
		:nominal_resolution = "100 km" ;
		:physics_index = "p1" ;
		:product = "model-output" ;
		:realization_index = "r1" ;
		:realm = "atmos" ;
		:region = "glb" ;
		:source = "ACCESS-ESM1-6: aerosol: classic; atmosphere: um7-3; land-surface: cable3; ocean-biogeochemistry: wombatlite; ocean: mom5; sea-ice: cice5" ;
		:source_id = "ACCESS-ESM1-6" ;
		:table_info = "Name: CMIP7_atmos.json; Creation Date:(2026-07-21 13:16:24) MD5:28339aa355908b9331150e1536f5e384" ;
		:temporal_label = "tavg" ;
		:title = "ACCESS-ESM1-6 output prepared for CMIP7" ;
		:tracking_id = "hdl:21.14107/56ae9743-13c7-4712-bf75-b445d0a7df7b" ;
		:variable_id = "hfls" ;
		:variant_label = "r1i1p1f1" ;
		:vertical_label = "u" ;
		:license = "CC-BY-4.0; CMIP7 data produced by MOHC is licensed under a Creative Commons Attribution 4.0 International License (https://creativecommons.org/licenses/by/4.0). Consult https://wcrp-cmip.github.io/cmip7-guidance/docs/CMIP7/Guidance_for_users/#2-terms-of-use-and-citations-requirements for terms of use governing CMIP7 output, including citation requirements and proper acknowledgment. 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." ;
		:cmor_version = "3.15.2" ;
data:

 time = 15.5, 45.5 ;

 time_bnds =
  0, 31,
  31, 60 ;

 x = 0, 10000, 20000, 30000 ;

 x_bnds =
  -5000, 5000,
  5000, 15000,
  15000, 25000,
  25000, 35000 ;

 y = 0, 10000, 20000 ;

 y_bnds =
  -5000, 5000,
  5000, 15000,
  15000, 25000 ;

 lambert_conformal_conic = _ ;

 latitude =
  10, 20, 30,
  8, 18, 28,
  6, 16, 26,
  4, 14, 24 ;

 longitude =
  280, 282, 284,
  290, 292, 294,
  300, 302, 304,
  310, 312, 314 ;

 vertices_latitude =
  5, 6, 15, 14,
  15, 16, 25, 24,
  25, 26, 35, 34,
  3, 4, 13, 12,
  13, 14, 23, 22,
  23, 24, 33, 32,
  1, 2, 11, 10,
  11, 12, 21, 20,
  21, 22, 31, 30,
  -1, 0, 9, 8,
  9, 10, 19, 18,
  19, 20, 29, 28 ;

 vertices_longitude =
  275, 285, 285, 275,
  277, 287, 287, 277,
  279, 289, 289, 279,
  285, 295, 295, 285,
  287, 297, 297, 287,
  289, 299, 299, 289,
  295, 305, 305, 295,
  297, 307, 307, 297,
  299, 309, 309, 299,
  305, 315, 315, 305,
  307, 317, 317, 307,
  309, 319, 319, 309 ;

 hfls =
  80, 88, 96,
  82, 90, 98,
  84, 92, 100,
  86, 94, 102,
  81, 89, 97,
  83, 91, 99,
  85, 93, 101,
  87, 95, 103 ;
}


Example 7: Fixed Field

Click to expand Python code
#!/usr/bin/env python3

from __future__ import annotations

import argparse
import json
import os
from pathlib import Path

import cmor
import numpy as np

EXAMPLE_DIR = Path(__file__).resolve().parent
RUN_DIR = Path.cwd()
DEFAULT_TABLES_PATH = RUN_DIR / "cmip7-cmor-tables" / "tables"
TABLES_PATH = Path(os.environ.get("CMOR_TABLES_PATH", DEFAULT_TABLES_PATH))
INPUT_PATH = EXAMPLE_DIR.parent / "CMIP7_input_example.json"


def configure(
    output_dir: Path,
    frequency: str = "mon",
    realization_index: str = "r1",
    forcing_index: str = "f1",
) -> None:
    output_dir.mkdir(parents=True, exist_ok=True)
    user_input = json.loads(INPUT_PATH.read_text())
    user_input["outpath"] = str(output_dir)
    user_input["frequency"] = frequency
    user_input["realization_index"] = realization_index
    user_input["forcing_index"] = forcing_index
    input_path = output_dir / "CMIP7_input_example.json"
    input_path.write_text(json.dumps(user_input, indent=2, sort_keys=True))
    cmor.setup(inpath=str(TABLES_PATH), netcdf_file_action=cmor.CMOR_REPLACE)
    cmor.dataset_json(str(input_path))


def apply_cmip7_variable_metadata(
    var_id: int,
    realm: str,
    table_entry: str,
    frequency: str,
    region: str,
) -> str:
    compound_name = ".".join(
        [realm] + table_entry.split("_") + [frequency, region]
    )

    with (TABLES_PATH / "CMIP7_cell_measures.json").open() as handle:
        cell_measures = json.load(handle)["cell_measures"]
    cmor.set_variable_attribute(
        var_id,
        "cell_measures",
        "c",
        cell_measures.get(compound_name, ""),
    )

    with (TABLES_PATH / "CMIP7_long_name_overrides.json").open() as handle:
        long_name_overrides = json.load(handle)["long_name_overrides"]
    if compound_name in long_name_overrides:
        cmor.set_variable_attribute(
            var_id,
            "long_name",
            "c",
            long_name_overrides[compound_name],
        )

    return compound_name


def write_example(output_dir: Path) -> str:
    configure(output_dir, frequency="fx")
    cmor.load_table("CMIP7_land.json")
    lat_id = cmor.axis(
        "latitude",
        "degrees_north",
        coord_vals=np.array([10.0, 20.0, 30.0], dtype="d"),
        cell_bounds=np.array([5.0, 15.0, 25.0, 35.0], dtype="d"),
    )
    lon_id = cmor.axis(
        "longitude",
        "degrees_east",
        coord_vals=np.array([0.0, 90.0, 180.0, 270.0], dtype="d"),
        cell_bounds=np.array(
            [-45.0, 45.0, 135.0, 225.0, 315.0],
            dtype="d",
        ),
    )
    var_id = cmor.variable(
        "rootd_ti-u-hxy-lnd",
        "m",
        [lat_id, lon_id],
        missing_value=1.0e20,
    )
    apply_cmip7_variable_metadata(
        var_id,
        "land",
        "rootd_ti-u-hxy-lnd",
        "fx",
        "glb",
    )
    data = np.array(
        [
            [0.50, 0.45, 1.0e20, 0.55],
            [0.60, 0.60, 1.0e20, 0.55],
            [1.0e20, 0.45, 0.50, 0.50],
        ],
        dtype="f4",
    )
    cmor.write(var_id, data)
    path = cmor.close(var_id, file_name=True)
    cmor.close()
    return path


def main() -> None:
    parser = argparse.ArgumentParser(
        description="Write CMIP7 example 7 with CMOR."
    )
    parser.add_argument(
        "--output-dir",
        type=Path,
        default=Path(__file__).resolve().parent / "output",
    )
    args = parser.parse_args()
    print(write_example(args.output_dir))


if __name__ == "__main__":
    main()

Click to expand NetCDF dump
netcdf rootd_ti-u-hxy-lnd_fx_glb_g999_ACCESS-ESM1-6_amip_r1i1p1f1 {
dimensions:
	lat = 3 ;
	lon = 4 ;
	bnds = 2 ;
variables:
	double lat(lat) ;
		lat:bounds = "lat_bnds" ;
		lat:units = "degrees_north" ;
		lat:axis = "Y" ;
		lat:long_name = "Latitude" ;
		lat:standard_name = "latitude" ;
	double lat_bnds(lat, bnds) ;
	double lon(lon) ;
		lon:bounds = "lon_bnds" ;
		lon:units = "degrees_east" ;
		lon:axis = "X" ;
		lon:long_name = "Longitude" ;
		lon:standard_name = "longitude" ;
	double lon_bnds(lon, bnds) ;
	float rootd(lat, lon) ;
		rootd:standard_name = "root_depth" ;
		rootd:long_name = "Maximum Root Depth" ;
		rootd:units = "m" ;
		rootd:cell_methods = "area: mean where land" ;
		rootd:missing_value = 1.e+20f ;
		rootd:_FillValue = 1.e+20f ;
		rootd:cell_measures = "area: areacella" ;

// global attributes:
		:Conventions = "CF-1.12" ;
		:activity_id = "CMIP" ;
		:archive_id = "WCRP" ;
		:area_label = "lnd" ;
		:branded_variable = "rootd_ti-u-hxy-lnd" ;
		:branding_suffix = "ti-u-hxy-lnd" ;
		:creation_date = "2026-08-07T00:46:52Z" ;
		:data_specs_version = "MIP-DS7.1.0.0" ;
		:description = "Simulation of the climate of the recent past with prescribed sea surface temperatures and sea ice concentrations." ;
		:drs_specs = "MIP-DRS7" ;
		:experiment = "Simulation of the climate of the recent past with prescribed sea surface temperatures and sea ice concentrations." ;
		:experiment_id = "amip" ;
		:external_variables = "areacella" ;
		:forcing_index = "f1" ;
		:frequency = "fx" ;
		:grid_label = "g999" ;
		:history = "2026-08-07T00:46:52Z ; CMOR rewrote data to be consistent with CF-1.12 and CMIP7 data requirements." ;
		:horizontal_label = "hxy" ;
		:host_collection = "CMIP7" ;
		:initialization_index = "i1" ;
		:institution = "Met Office Hadley Centre" ;
		:institution_id = "MOHC" ;
		:license_id = "CC-BY-4.0" ;
		:mip_era = "CMIP7" ;
		:nominal_resolution = "100 km" ;
		:physics_index = "p1" ;
		:product = "model-output" ;
		:realization_index = "r1" ;
		:realm = "land" ;
		:region = "glb" ;
		:source = "ACCESS-ESM1-6: aerosol: classic; atmosphere: um7-3; land-surface: cable3; ocean-biogeochemistry: wombatlite; ocean: mom5; sea-ice: cice5" ;
		:source_id = "ACCESS-ESM1-6" ;
		:table_info = "Name: CMIP7_land.json; Creation Date:(2026-07-21 13:16:24) MD5:ea523449976b1c3cc3c14c1492f2fb74" ;
		:temporal_label = "ti" ;
		:title = "ACCESS-ESM1-6 output prepared for CMIP7" ;
		:tracking_id = "hdl:21.14107/59f55805-e290-4366-a99a-8f6ef9c20df6" ;
		:variable_id = "rootd" ;
		:variant_label = "r1i1p1f1" ;
		:vertical_label = "u" ;
		:license = "CC-BY-4.0; CMIP7 data produced by MOHC is licensed under a Creative Commons Attribution 4.0 International License (https://creativecommons.org/licenses/by/4.0). Consult https://wcrp-cmip.github.io/cmip7-guidance/docs/CMIP7/Guidance_for_users/#2-terms-of-use-and-citations-requirements for terms of use governing CMIP7 output, including citation requirements and proper acknowledgment. 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." ;
		:cmor_version = "3.15.2" ;
data:

 lat = 10, 20, 30 ;

 lat_bnds =
  5, 15,
  15, 25,
  25, 35 ;

 lon = 0, 90, 180, 270 ;

 lon_bnds =
  -45, 45,
  45, 135,
  135, 225,
  225, 315 ;

 rootd =
  0.5, 0.45, _, 0.55,
  0.6, 0.6, _, 0.55,
  _, 0.45, 0.5, 0.5 ;
}