Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
76025d6
Created python implimentaions for cmorizing MODIS terra and aqua data…
noahfruttarol Feb 17, 2026
7702714
Applied ruff format changes
noahfruttarol Feb 17, 2026
3f94595
ok
noahfruttarol Feb 17, 2026
2d1228c
1
noahfruttarol Feb 18, 2026
2de95e9
fixed problems running the cmorizer
noahfruttarol Feb 19, 2026
4cd8575
Bunched cmorized data files by year
noahfruttarol Feb 19, 2026
b402204
reverted esmvaltool/cmorizers/data/formatters/datasets/misr.py
noahfruttarol Feb 19, 2026
ffd2efe
added exspected data for cmip6 standard
noahfruttarol Feb 24, 2026
e61815d
Merge branch 'ESMValGroup:main' into MISR-CMORization-script
noahfruttarol Feb 24, 2026
8391ffe
Merge branch 'ESMValGroup:main' into MODIS_cmorize_update
noahfruttarol Feb 26, 2026
e487ed0
Merge branch 'ESMValGroup:main' into MISR-CMORization-script
noahfruttarol Mar 2, 2026
8136ccb
Merge branch 'main' into MISR-CMORization-script
noahfruttarol Mar 5, 2026
7037ee7
fixed project_id
noahfruttarol Mar 5, 2026
e0b8a3c
Merge branch 'ESMValGroup:main' into MISR-CMORization-script
noahfruttarol Mar 12, 2026
0a19731
Minor changes and added reference
noahfruttarol Mar 12, 2026
0a8f443
added MISR to check recipe
noahfruttarol Mar 12, 2026
1272a68
Merge branch 'main' into MISR-CMORization-script
noahfruttarol Mar 13, 2026
7c9d2a2
added dataset to ESMValTool/doc/sphinx/source/input.rst
noahfruttarol Mar 16, 2026
e21d1a4
Merge branch 'main' into MISR-CMORization-script
valeriupredoi Mar 26, 2026
8e5f75e
Merge branch 'ESMValGroup:main' into all-work
noahfruttarol Mar 26, 2026
70a829e
Merge branch 'all-work' into MISR-CMORization-script
noahfruttarol Mar 26, 2026
2452957
Revert changes made by mistake.
noahfruttarol Apr 21, 2026
50ca81b
Merge branch 'ESMValGroup:main' into MISR-CMORization-script
noahfruttarol Apr 21, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions doc/sphinx/source/input.rst
Original file line number Diff line number Diff line change
Expand Up @@ -424,6 +424,8 @@ A list of the datasets for which a CMORizers is available is provided in the fol
+----------------------------------------+------------------------------------------------------------------------------------------------------+------+-----------------+
| MLS-AURA* [#t3]_ | hur, hurStderr (day) | 3 | Python |
+----------------------------------------+------------------------------------------------------------------------------------------------------+------+-----------------+
| MISR | od550aer, abs550aer (AERmon) | 3 | Python |
+----------------------------------------+------------------------------------------------------------------------------------------------------+------+-----------------+
| MOBO-DIC-MPIM | dissic (Omon) | 2 | Python |
+----------------------------------------+------------------------------------------------------------------------------------------------------+------+-----------------+
| MOBO-DIC2004-2019 | dissic (Omon) | 2 | Python |
Expand Down
19 changes: 19 additions & 0 deletions esmvaltool/cmorizers/data/cmor_config/MISR.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Global attributes of NetCDF file
attributes:
project_id: OBS6
dataset_id: MISR
modeling_realm: atmos
type: sat
tier: 3
version: 'V0032'
source: 'https://asdc.larc.nasa.gov/project/MISR/MIL3MAEN_004'
reference: 'misr'
files: 'MISR_AM1_CGAS_*_F15_0032.nc'
# Variables to CMORize
variables:
od550aer:
raw_name: Aerosol_Optical_Depth
mip: AERmon
abs550aer:
raw_name: Absorbing_Optical_Depth
mip: AERmon
10 changes: 10 additions & 0 deletions esmvaltool/cmorizers/data/datasets.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1082,6 +1082,16 @@ datasets:
info: |
Download the file MPI_MOBO-DIC_2004-2019_v2.nc

MISR:
tier: 3
source: https://asdc.larc.nasa.gov/project/MISR/MIL3MAEN_004
last_access: 2026-02-17
info: |
To obtain the data set you will need to go to the Data Distribution
section of the link, create an account/sign in on the earthdata.nasa.gov website
and then follow the instructions for downloading the data.
The data is currently freely available, but a registration is required.

MODIS:
tier: 3
source: https://ladsweb.modaps.eosdis.nasa.gov/search/order
Expand Down
166 changes: 166 additions & 0 deletions esmvaltool/cmorizers/data/formatters/datasets/misr.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
"""ESMValTool CMORizer for MISR data.

Tier
Tier 3

Source
ftp://l5ftl01.larc.nasa.gov/MISR/MIL3MAEN.004/
"""

import copy
import datetime as dt
import logging
import os
from pathlib import Path

import numpy as np
import xarray as xr
from dask import array as da
from esmvalcore.cmor.table import CMOR_TABLES

from esmvaltool.cmorizers.data import utilities as utils

logger = logging.getLogger(__name__)

band550 = {"name": "green_558nm", "lambda": 558}


def _extract_variable(short_name, var, cfg, in_dir, out_dir):
attrs = copy.deepcopy(cfg["attributes"])
attrs["mip"] = var["mip"]
ver = attrs["version"]
files = attrs["files"]
raw_var = var.get("raw_name", short_name)

cmor_table = CMOR_TABLES[attrs["project_id"]]
cmor_info = cmor_table.get_variable(var["mip"], short_name)

"""Extract variable."""
# load data
logger.debug(
"Loading data from file(s) '%s' in directory '%s' with version '%s'",
files,
in_dir,
ver,
)
for filepath in Path(os.path.join(in_dir)).glob(files):
xrds = xr.open_dataset(filepath, group="Aerosol_Parameter_Average")
xrvar = xrds.sel(Band=band550["name"], Optical_Depth_Range="all")[
raw_var
]

# change order of latitude and longitude coordinates
# xrvar = xrvar.transpose()

# Add additional coordinates before converting to an iris cube, as this is easier with xarray

# Time not present in source data, needs to be added manually
# Determine time from filename:
fileparts = str(filepath).split("_")
year = int(fileparts[-3])
monthstr = fileparts[-4]
month = [
"JAN",
"FEB",
"MAR",
"APR",
"MAY",
"JUN",
"JUL",
"AUG",
"SEP",
"OCT",
"NOV",
"DEC",
].index(monthstr) + 1
days_since_1850 = dt.date(year, month, 15) - dt.date(1850, 1, 1)
lb_since_1850 = dt.date(year, month, 1) - dt.date(1850, 1, 1)
if month == 12:
ub_since_1850 = dt.date(year + 1, 1, 1) - dt.date(1850, 1, 1)
else:
ub_since_1850 = dt.date(year, month + 1, 1) - dt.date(1850, 1, 1)

xrvar = xrvar.assign_coords(time=days_since_1850.days)
xrvar = xrvar.expand_dims("time", axis=2)
xrvar["time"].attrs["units"] = "days since 1850-01-01 00:00:00"

if short_name in ["od550aer", "abs550aer"]:
xrvar = xrvar.assign_coords(radiation_wavelength=band550["lambda"])
xrvar["radiation_wavelength"].attrs["units"] = "nm"

cube = xrvar.to_iris()

# Fix metadata
cube.coord("Geodetic Latitude").rename("latitude")
cube.coord("Geodetic Longitude").rename("longitude")

# add time bounds
cube.coord("time").bounds = np.array(
[ub_since_1850.days, lb_since_1850.days]
)

utils.fix_var_metadata(cube, cmor_info)
utils.set_global_atts(cube, attrs)

utils.fix_dim_coordnames(cube)

# When Dask tries to roll this cube, it fails because it can't chunk this properly
# So here we replicate the part of fix_coords that does that, except with numpy.roll
# instead of dask.roll.
# However, it seems that those last two lines had no effect on the results...
# cube.data has shape (360, 720, 1) i.e. (lat, lon, time)
# so the original code tried to roll the cube on the time axis
# I could hard-code the lon axis (like they did), but instead I try to autodetect it.
cube_coord = cube.coord("longitude")
logger.debug("Fixing longitude...")
if cube_coord.ndim == 1:
if cube_coord.points[0] < 0.0 and cube_coord.points[-1] < 181.0:
cube_coord.points = cube_coord.points + 180.0
cube.attributes["geospatial_lon_min"] = 0.0
cube.attributes["geospatial_lon_max"] = 360.0
nlon = len(cube_coord.points)
axis = cube.coords().index(cube_coord)
shift = nlon // 2
cube.data = da.roll(cube.core_data(), shift, axis=axis)

if np.diff(cube.coord("latitude").points)[0] < 0:
# convert [90,-90] to [-90,90]
cube.coord("latitude").points = cube.coord("latitude").points[::-1]
# flip the data
cube.data = cube.data[:, ::-1, :] # latitude is axis=1

lat_bounds = []
for lat in cube.coord("latitude").points:
lat_bounds.append([lat - 0.25, lat + 0.25])
lat_bounds = np.array(lat_bounds)
cube.coord("latitude").bounds = lat_bounds.reshape(-1, 2)

lon_bounds = []
for lon in cube.coord("longitude").points:
lon_bounds.append([lon - 0.25, lon + 0.25])
lon_bounds = np.array(lon_bounds)
cube.coord("longitude").bounds = lon_bounds.reshape(-1, 2)

utils.fix_coords(cube)

# fix the wavelength coordinate information.
if short_name in ["od550aer", "abs550aer"]:
cube.coord("radiation_wavelength").var_name = "wavelength"
cube.coord("wavelength").standard_name = "radiation_wavelength"

utils.set_global_atts(cube, attrs)

# Save variable
logger.debug(f"Saving Cube: {cube}, in directory: {out_dir}")
utils.save_variable(
cube, short_name, out_dir, attrs, unlimited_dimensions=["time"]
)


def cmorization(in_dir, out_dir, cfg, cfg_user, start_date, end_date):
"""Run CMORizer for MISR."""
cfg.pop("cmor_table")

for short_name, var in cfg["variables"].items():
logger.info(f"CMORizing variable '{short_name}'")
_extract_variable(short_name, var, cfg, in_dir, out_dir)
4 changes: 4 additions & 0 deletions esmvaltool/config-references.yml
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,10 @@ authors:
name: Winterstein, Franziska
institute: DLR, Germany
orcid: https://orcid.org/0000-0002-2406-4936
fruttarol_noah:
name: Fruttarol, Noah
institute: CCCma, ECCC, Canada
github: noahfruttarol
fuckar_neven:
name: Fuckar, Neven
institute: BSC, Spain
Expand Down
13 changes: 13 additions & 0 deletions esmvaltool/recipes/examples/recipe_check_obs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -800,6 +800,19 @@ diagnostics:
scripts: null


MISR:
description: MISR check
variables:
od550aer:
mip: AERmon
abs550aer:
mip: AERmon
additional_datasets:
- {dataset: MISR, project: OBS, mip: AERmon, tier: 3,
type: sat, version: v0032, start_year: 2000, end_year: 2020}
scripts: null


MOBO-DIC-MPIM:
description: MOBO-DIC-MPIM check
variables:
Expand Down
11 changes: 11 additions & 0 deletions esmvaltool/references/misr.bibtex
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
@misc{misr,
doi = {10.5067/Terra/MISR/MIL3MAEN_L3.004},
url = {https://doi.org/10.5067/Terra/MISR/MIL3MAEN_L3.004},
publisher = {NASA Langley Atmospheric Science Data Center DAAC},
title = {MISR Level 3 Component Global Aerosol product in netCDF format covering a month V004},
author = {NASA/LARC/SD/ASDC},
date = {2008-09-26},
year = 2008,
month = 9,
day = 26,
}