This document details the reverse-engineering analysis of how 3D MERRA-2 NetCDF files (on a MERRA-2.inst3_3d_asm_Nv.monthly.YYYYMM.0001x0091x0072.nc4 on a
It includes comparison between two input sources:
- Pre-made Monthly Means (
MERRA-2.inst3_3d_asm_Nv.monthly.YYYYMM.nc4) - Daily Instantaneous Files (
MERRA2_400.inst3_3d_asm_Nv.YYYYMMDD.nc4, 3-hourly, 240 timesteps/month)
The June 2012 validation changes an important earlier conclusion: the dominant difference from the canonical ozone field is a unit conversion, not numerical precision or the use of monthly rather than daily input. The canonical field is approximately the zonal mean of MERRA-2 mass mixing ratio converted to volume (mole) mixing ratio:
Here 28.97 and 48.0 are the molecular weights of dry air and ozone, respectively. The canonical per-month files retain O3:units = "kg kg-1", but the downstream pchem.species...MERRA2OX file correctly identifies the resulting OX field as mol mol-1.
| Dimension | Original Parent File | Zonal Mean File (0001x0091x0072) |
Transformation Method |
|---|---|---|---|
Longitude (lon) |
|
|
Unweighted arithmetic mean across all 576 longitudes |
Latitude (lat) |
|
|
Direct sub-sampling (lat[::4]) |
Vertical Level (lev) |
1..72) |
|
Layer index coordinate populated with hPa pressure values |
- Pros: Fastest processing (under 2 seconds per month), small disk I/O (single 800 MB file).
-
Accuracy: For June 2012, the longitude mean alone differs from canonical O3 by up to
$1.399 \times 10^{-5}$ ; this is expected because it is still inkg kg-1while canonical O3/OX is effectively inmol mol-1. -
After conversion: multiplying by
$28.97/48.0$ reduces the June 2012 maximum absolute residual to$1.407 \times 10^{-8}$ and the mean absolute residual to$3.50 \times 10^{-10}$ .
- Pros: Allows an independent check of the temporal averaging and is appropriate if a monthly product is unavailable or does not use the needed sampling/masking convention.
-
June 2012 result: mean over all 240 three-hourly samples and 576 longitudes agrees with the zonal mean of the pre-made monthly field to within
$3.64 \times 10^{-12}$ before the molecular-weight conversion. - Conclusion for O3: daily input does not explain the canonical discrepancy for this month. Time and longitude means commute for these data; use the monthly field unless a future validation finds a non-linear mask or weighting difference.
- I/O Overhead: Requires opening 30 daily files (~24 GB total input per month).
An unweighted arithmetic mean is performed over all 576 longitudes:
Latitude values are sampled directly at a stride of 4 from the 361-point grid down to the 91-point grid:
This selects latitudes
The field to compare with the legacy OX forcing file is a volume mixing ratio, not the native MERRA-2 O3 mass mixing ratio:
For a strict replacement workflow, apply this conversion after the longitude mean and before writing OX. Preserve the target file's pressure-level coordinate and latitude units (radians in pchem.species.CMIP-5.MERRA2OX.197902-201706.z_91x72.nc4).
All figures below use the 72 by 91 O3/OX grid (6,552 values). daily is the mean over the 30 June daily files, all 240 three-hourly samples, and all 576 longitudes. monthly is the longitude mean of MERRA-2.inst3_3d_asm_Nv.monthly.201206.nc4; both are still mass mixing ratio until the indicated conversion.
| Comparison | Maximum absolute difference | Mean absolute difference | RMSE | 99th-percentile absolute difference | Maximum relative difference |
|---|---|---|---|---|---|
| daily O3 minus monthly O3 | |||||
| monthly O3 |
|||||
| daily O3 |
|||||
canonical O3 versus legacy pchem OX (same new-level grid) |
0 | 0 | 0 | 0 | 0 |
For daily minus monthly O3, 55 of 6,552 values exceed
Operational recommendation. Use the pre-made monthly 3D field for production zonal means. This is established for June 2012, not a mathematical guarantee for every future MERRA collection: retain a one-month daily-vs-monthly sentinel test when changing MERRA collection/version, variable, missing-value treatment, or temporal weighting.
import xarray as xr
# Open parent 3D monthly file
ds = xr.open_dataset('MERRA-2.inst3_3d_asm_Nv.monthly.201206.nc4', decode_times=False)
# 1. Select native O3 mass mixing ratio.
# 2. Arithmetic mean across longitude.
# 3. Convert mass mixing ratio to volume mixing ratio.
# 4. Sub-sample latitude (361 -> 91).
ox = (ds['O3'].mean(dim='lon') * (28.97 / 48.0)).isel(lat=slice(None, None, 4))
ox.name = 'OX'
ox.attrs.update(units='mol mol-1', long_name='odd_oxygen_volume_mixing_ratio')
ds_o3 = ox.to_dataset()
# 5. Expand lon dimension to length 1 (-180.0) & restore dimension order
ds_o3 = ds_o3.expand_dims(lon=[-180.0]).transpose('time', 'lev', 'lat', 'lon')
# Save to NetCDF4
ds_o3.to_netcdf('MERRA-2.inst3_3d_asm_Nv.monthly.201206.0001x0091x0072.OX.python.nc4', format='NETCDF4_CLASSIC')import xarray as xr
import glob
# Find all 30 daily files for the target month
daily_files = sorted(glob.glob('/discover/nobackup/projects/gmao/merra2/data/pub/products/MERRA2_400/Y2012/M06/MERRA2_400.inst3_3d_asm_Nv.201206*.nc4'))
# Open multi-file dataset across all 240 timesteps
ds_daily = xr.open_mfdataset(daily_files, combine='by_coords')
# Mean across both time and longitude, convert units, then sub-sample latitude
ox = (ds_daily['O3'].mean(dim=['time', 'lon']) * (28.97 / 48.0)).isel(lat=slice(None, None, 4))
ox.name = 'OX'
ox.attrs.update(units='mol mol-1', long_name='odd_oxygen_volume_mixing_ratio')
ds_o3 = ox.to_dataset()
ds_o3 = ds_o3.expand_dims(time=[ds_daily.time.values[0]], lon=[-180.0])
ds_o3 = ds_o3.transpose('time', 'lev', 'lat', 'lon')
# Save to NetCDF4
ds_o3.to_netcdf('MERRA-2.inst3_3d_asm_Nv.monthly.201206.0001x0091x0072.OX.from_dailies.nc4', format='NETCDF4_CLASSIC')cdo -mulc,0.6035416666666667 -chname,O3,OX -select,name=O3 -zonmean -samplegrid,4 \
MERRA-2.inst3_3d_asm_Nv.monthly.201206.nc4 \
MERRA-2.inst3_3d_asm_Nv.monthly.201206.0001x0091x0072.OX.cdo.nc4cdo -mulc,0.6035416666666667 -chname,O3,OX -select,name=O3 -zonmean -samplegrid,4 -ensmean \
/discover/nobackup/projects/gmao/merra2/data/pub/products/MERRA2_400/Y2012/M06/MERRA2_400.inst3_3d_asm_Nv.201206*.nc4 \
MERRA-2.inst3_3d_asm_Nv.monthly.201206.0001x0091x0072.OX.cdo_dailies.nc4export PATH=/discover/nobackup/projects/gmao/share/dasilva/opengrads/Contents:$PATH
lats4d.sh -i MERRA-2.inst3_3d_asm_Nv.monthly.201206.nc4 \
-o MERRA-2.inst3_3d_asm_Nv.monthly.201206.0001x0091x0072.O3.lats4d \
-vars o3 \
-func "(28.97/48.0)*ave(@,x=1,x=576)" \
-ftype sdf
nccopy -d 5 MERRA-2.inst3_3d_asm_Nv.monthly.201206.0001x0091x0072.O3.lats4d.nc \
MERRA-2.inst3_3d_asm_Nv.monthly.201206.0001x0091x0072.O3.lats4d.nc4
rm MERRA-2.inst3_3d_asm_Nv.monthly.201206.0001x0091x0072.O3.lats4d.nc- Global Attributes:
Title:"GEOS-5 GCM (DTOA Conversion)"History:"File written by GFIO v1.0.8"
- Software Chain:
These attributes are automatically assigned by GrADS /
lats4d(Arlindo da Silva's LATS-based NetCDF exporter tool built on GMAO's GFIO library) or the GMAOdtoa(Data Transfer / Output Assembly) post-processing tool. - The control file
xdf.tablwas used by OpenGrADS to template and access these monthly zonal-mean files (%y4%m2).
When running nccmp -dmfgsB between generated files and canonical files:
-
Format & Metadata:
nccmpflags global attribute differences (Source,Contact,Title,History) and NetCDF attribute conventions (_FillValue=nanvs1.0e15f). -
Data interpretation: an unconverted O3 comparison is invalid because it compares
kg kg-1to the legacy OX-likemol mol-1field. The June 2012 unconverted maximum difference is$1.399 \times 10^{-5}$ , not float32 roundoff. -
Legacy forcing-file check: June 2012
OXinpchem.species.CMIP-5.MERRA2OX.197902-201706.z_91x72.nc4is bit-for-bit identical to the corresponding legacy canonical OX/new-levels field (6,552 of 6,552 values). Thus replacing OX requires preserving itsmol mol-1convention.