iris: Derived coordinates are not loaded anymore?

📰 Custom Issue

Hey guys, one more for me today, I am using this file and loading it simple:

import iris

cs = iris.load("/home/valeriu/ESMValCore/tests/integration/cmor/_fixes/test_data/common_cl_ap.nc")
for c in cs:
    print(c)

with iris=3 I am not seeing the derived coordinates no more:

  • with iris=2.4:
b_bnds / (1)                        (atmosphere_hybrid_sigma_pressure_coordinate: 2; -- : 2)
     Dimension coordinates:
          atmosphere_hybrid_sigma_pressure_coordinate                           x       -
     Auxiliary coordinates:
          ap                                                                    x       -
          b                                                                     x       -
     Derived coordinates:
          air_pressure                                                          x       -
ap_bnds / (1)                       (atmosphere_hybrid_sigma_pressure_coordinate: 2; -- : 2)
     Dimension coordinates:
          atmosphere_hybrid_sigma_pressure_coordinate                           x       -
     Auxiliary coordinates:
          ap                                                                    x       -
          b                                                                     x       -
     Derived coordinates:
          air_pressure                                                          x       -
cloud_area_fraction_in_atmosphere_layer    (time: 1; atmosphere_hybrid_sigma_pressure_coordinate: 2; latitude: 3; longitude: 4)
     Dimension coordinates:
          time                                            x                                               -            -             -
          atmosphere_hybrid_sigma_pressure_coordinate     -                                               x            -             -
          latitude                                        -                                               -            x             -
          longitude                                       -                                               -            -             x
     Auxiliary coordinates:
          surface_air_pressure                            x                                               -            x             x
          ap                                              -                                               x            -             -
          b                                               -                                               x            -             -
     Derived coordinates:
          air_pressure                                    x                                               x            x             x
surface_air_pressure / (Pa)         (time: 1; latitude: 3; longitude: 4)
     Dimension coordinates:
          time                           x            -             -
          latitude                       -            x             -
          longitude                      -            -             x
     Attributes:
          additional_attribute: xyz
  • with iris=3.0:
b_bnds / (unknown)                  (atmosphere_hybrid_sigma_pressure_coordinate: 2; -- : 2)
     Dimension coordinates:
          atmosphere_hybrid_sigma_pressure_coordinate                           x       -
     Auxiliary coordinates:
          ap                                                                    x       -
          b                                                                     x       -
ap_bnds / (unknown)                 (atmosphere_hybrid_sigma_pressure_coordinate: 2; -- : 2)
     Dimension coordinates:
          atmosphere_hybrid_sigma_pressure_coordinate                           x       -
     Auxiliary coordinates:
          ap                                                                    x       -
          b                                                                     x       -
cloud_area_fraction_in_atmosphere_layer       (time: 1; atmosphere_hybrid_sigma_pressure_coordinate: 2; latitude: 3; longitude: 4)
     Dimension coordinates:
          time                                            x                                               -            -             -
          atmosphere_hybrid_sigma_pressure_coordinate     -                                               x            -             -
          latitude                                        -                                               -            x             -
          longitude                                       -                                               -            -             x
     Auxiliary coordinates:
          surface_air_pressure                            x                                               -            x             x
          ap                                              -                                               x            -             -
          b                                               -                                               x            -             -
surface_air_pressure / (Pa)         (time: 1; latitude: 3; longitude: 4)
     Dimension coordinates:
          time                           x            -             -
          latitude                       -            x             -
          longitude                      -            -             x
     Attributes:
          additional_attribute: xyz

where did the Derived coordinates go?

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Comments: 64 (45 by maintainers)

Most upvoted comments

@bouweandela @valeriupredoi @schlunma @jvegasbsc

iris 3.0.1 has already been tagged, the docs have already been built on readthedocs, and I’ve just kicked of an update on the conda-forge iris-feedstock… almost there 😉

I’ll ping the ESMValTool WhatsApp thread when conda-forge has pushed the built iris 3.0.1 package to the Anaconda Cloud channel - then you can install and test. Good luck! 🤞

LOL what a muppet… hang tight…

Ha! Found one: /CMIP6/CMIP/BCC/BCC-ESM1/historical/r1i1p1f1/Amon/cl/gn/v20181217/cl_Amon_BCC-ESM1_historical_r1i1p1f1_gn_185001-201412.nc

netcdf cl_Amon_BCC-ESM1_historical_r1i1p1f1_gn_185001-201412 {
dimensions:
        time = UNLIMITED ; // (1980 currently)
        lev = 26 ;
        lat = 64 ;
        lon = 128 ;
        bnds = 2 ;
variables:
        double time(time) ;
                time:bounds = "time_bnds" ;
                time:units = "days since 1850-01-01" ;
                time:calendar = "365_day" ;
                time:axis = "T" ;
                time:long_name = "time" ;
                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:long_name = "vertical coordinate formula term: reference pressure" ;
                p0:units = "Pa" ;
        double a(lev) ;
                a:long_name = "vertical coordinate formula term: a(k)" ;
        double b(lev) ;
                b:long_name = "vertical coordinate formula term: b(k)" ;
        float ps(time, lat, lon) ;
                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 = "Cloud Area Fraction" ;
                cl:comment = "Percentage cloud cover, including both large-scale and convective cloud." ;
                cl:units = "%" ;
                cl:original_name = "CLOUD" ;
                cl:cell_methods = "area: time: mean (interval: 20 minutes)" ;
                cl:cell_measures = "area: areacella" ;
                cl:missing_value = 1.e+20f ;
                cl:_FillValue = 1.e+20f ;
                cl:history = "2018-12-17T02:20:59Z altered by CMOR: Inverted axis: lev." ;

@schlunma seems like a reasonable work around, but it’s really causing you guys quite a lot of extra work to jump through hoops.

I’ve got a fix that I’m testing just now that would gracefully promote any formula terms variable that must be dimensionless to be dimensionless if it has units of unknown - it’s doing this within the aux factory itself.

Also, this behavioural change applies to 6 aux factories - so for me, it makes more sense to fix this in iris rather than force this upon all users.

I’m going to roll with this fix regardlessly, as my expectation is that this will affect many users.

Question: do you have any other ESMValTool netCDF files that also show this failure that I can test my fix with?

@valeriupredoi Okay, I’m dropping everything today to service this - I can totally appreciate the situation you’re in, so I’m going to push an iris 3.0.1 release today.

Hopefully, I can turn this around quickly for you, as I know you’re working to a tight deadline.

I’ll ping you here @valeriupredoi with my progress on this to keep you in the loop 👍

@valeriupredoi Okay, so this took a wee bit of digging, but the mystery is thankfully solved 🕵️

Firstly, I can recreate your issue in iris 3.0.0, and I can also confirm that iris 2.4.0 does indeed realise derived coordinates for your example dataset 👍

This change in behaviour between iris 3.0.0 and 2.4.0 directly relates to PR #3795, where the units of an AuxCood or DimCoord that is not explicitly specified defaults to unknown rather then 1 (dimensionless).

We thought about this change in behaviour long and hard, as the treatment of default units was inconsistent across iris for different objects, and the CF Metadata standard did not add any clarity (no surprises there, indeed it was inconsistent), but in the end we unified the behaviour for consistency sake. That is, erring on the side that it correct and safer to assign units as unknown in the case when they are not specified, whereas it is potentially incorrect and not safe to assume default dimensionless units instead.

This change in behaviour is mentioned in the iris 3.0.0 release notes in the Incompatible Changes section, fifth bullet point.

Where this is stinging you is for the missing units for the sigma variable associated with the atmosphere_hybrid_sigma_pressure_coordinate i.e., given formula_terms = "ap: ap_bnds b: b_bnds ps: ps", sigma in this case is the NetCDF b variable, and this is defined within the example file simply as double b(lev) ;

As the b variable has no units associated with it iris 3.0.0 assigns the default units of unknown (which they are). However, during the creation of the auxiliary factory that creates the derived coordinate it enforces the contract that the sigma variable must be dimensionless, which in this case it’s not, as it’s unknown. Note that, enforcing that sigma is dimensionless has not changed from 2.4.0 to 3.0.0.

This failure for sigma units results in iris raising a warning and not creating the associated HybridPressureFactory auxiliary factory i.e., during loading you see the following warning:

UserWarning: Invalid units: sigma must be dimensionless.

Also, note the example given in the CF Metadata Conventions, where their sigma variable has explicit units = "1".

So in order to fix this, my advise is to add the correct units to your sigma variable in the file. In addition to this, you could also add the missing bounds attribute for the sigma (b) and delta (ap) variables e.g.,

netcdf common_cl_ap {
dimensions:
	time = 1 ;
	lev = 2 ;
	lat = 3 ;
	lon = 4 ;
	bnds = 2 ;
variables:
	double time(time) ;
		time:standard_name = "time" ;
		time:units = "days since 6543-2-1" ;
	double lev(lev) ;
		lev:bounds = "lev_bnds" ;
		lev:standard_name = "atmosphere_hybrid_sigma_pressure_coordinate" ;
		lev:units = "1" ;
		lev:formula_terms = "ap: ap b: b ps: ps" ;
	double lev_bnds(lev, bnds) ;
		lev_bnds:standard_name = "atmosphere_hybrid_sigma_pressure_coordinate" ;
		lev_bnds:units = "1" ;
		lev_bnds:formula_terms = "ap: ap_bnds b: b_bnds ps: ps" ;
	double lat(lat) ;
		lat:standard_name = "latitude" ;
		lat:units = "degrees_north" ;
	double lon(lon) ;
		lon:standard_name = "longitude" ;
		lon:units = "degrees_east" ;
	double b(lev) ;
                b:units = "1" ;                                                 <== add "units" for sigma
                b:bounds = "b_bnds" ;                                           <== add "bounds" for sigma
	double b_bnds(lev, bnds) ;
	double ps(time, lat, lon) ;
		ps:standard_name = "surface_air_pressure" ;
		ps:units = "Pa" ;
		ps:additional_attribute = "xyz" ;
	float cl(time, lev, lat, lon) ;
		cl:standard_name = "cloud_area_fraction_in_atmosphere_layer" ;
		cl:units = "%" ;
	double ap(lev) ;
		ap:units = "Pa" ;
                ap:bounds = "ap_bnds" ;                                         <== add "bounds" for delta
	double ap_bnds(lev, bnds) ;
}

Hope this helps 😄

Installed iris 3.0.1 and tested… seems to work for me 😉

Thanks for the amazingly quick bugfix and release! 🥳

legend @bjlittle cheers very much! 🍺

Cheers @bjlittle, I’m happy with it 👍 Thanks for your quick help!!

Unless you tell me otherwise, I’m cracking on with 3.0.1…

@valeriupredoi thanks, nuke the repo dude 👍

that’s terrific! Thanks very much @bjlittle - a very nice example of cross-package collaboration when you have cool devvs on both sides, remind me to buy you a 🍺 when all this mess is over (the pandemic not the ESGF data issues, the latter one won’t be over soon) 😁

The UKESM model should have an altitude derived coordinate, that’s weird!

EDIT: You need to use fname2 instead of fname in the beginning 😄

     Derived coordinates:
          air_pressure                                             x

yay! welcome to the absolute nightmare on ESGF street @bjlittle 🤣

Sweet, this looks good too…

>>> fname2 = "/downloads/cl_Amon_UKESM1-0-LL_historical_r1i1p1f2_gn_185001-189912.nc"
>>> cubes = iris.load(fname)
/net/home/h05/itwl/projects/git/iris/lib/iris/fileformats/cf.py:862: UserWarning: Missing CF-netCDF measure variable 'areacella', referenced by netCDF variable 'cl'
  message % (variable_name, nc_var_name)
/net/home/h05/itwl/projects/git/iris/lib/iris/fileformats/cf.py:1207: UserWarning: Ignoring formula terms variable 'ps' referenced by data variable 'b_bnds' via variable 'lev': Dimensions ('time', 'lat', 'lon') do not span ('lev', 'bnds')
  warnings.warn(msg)
/net/home/h05/itwl/projects/git/iris/lib/iris/fileformats/cf.py:1207: UserWarning: Ignoring formula terms variable 'ps' referenced by data variable 'a_bnds' via variable 'lev': Dimensions ('time', 'lat', 'lon') do not span ('lev', 'bnds')
  warnings.warn(msg)
/net/home/h05/itwl/projects/git/iris/lib/iris/fileformats/netcdf.py:685: UserWarning: Unable to find coordinate for variable 'ps'
  "{!r}".format(name)
/net/home/h05/itwl/projects/git/iris/lib/iris/fileformats/netcdf.py:685: UserWarning: Unable to find coordinate for variable 'ps'
  "{!r}".format(name)

>>> print(cubes)
0: vertical coordinate formula term: b(k+1/2) / (unknown) (atmosphere_hybrid_sigma_pressure_coordinate: 26; -- : 2)
1: vertical coordinate formula term: a(k+1/2) / (unknown) (atmosphere_hybrid_sigma_pressure_coordinate: 26; -- : 2)
2: Surface Air Pressure / (Pa)         (time: 1980; latitude: 64; longitude: 128)
3: cloud_area_fraction_in_atmosphere_layer / (%) (time: 1980; atmosphere_hybrid_sigma_pressure_coordinate: 26; latitude: 64; longitude: 128)

>>> print(cubes[3])
cloud_area_fraction_in_atmosphere_layer             (time: 1980; atmosphere_hybrid_sigma_pressure_coordinate: 26; latitude: 64; longitude: 128)
     Dimension coordinates:
          time                                                     x                                                  -             -              -
          atmosphere_hybrid_sigma_pressure_coordinate              -                                                  x             -              -
          latitude                                                 -                                                  -             x              -
          longitude                                                -                                                  -             -              x
     Auxiliary coordinates:
          Surface Air Pressure                                     x                                                  -             x              x
          vertical coordinate formula term: a(k)                   -                                                  x             -              -
          vertical coordinate formula term: b(k)                   -                                                  x             -              -
          vertical pressure                                        -                                                  x             -              -
     Derived coordinates:
          air_pressure                                             x                                                  x             x              x
     Scalar coordinates:
          vertical coordinate formula term: reference pressure: 100000.0 Pa
     Attributes:
          Conventions: CF-1.7 CMIP-6.2
          activity_id: CMIP
          branch_method: Standard
          branch_time_in_child: 0.0
          branch_time_in_parent: 2110.0
          cmor_version: 3.3.2
          comment: Percentage cloud cover, including both large-scale and convective clou...
          contact: Dr. Tongwen Wu(twwu@cma.gov.cn)
          creation_date: 2018-12-17T02:20:59Z
          data_specs_version: 01.00.27
          description: DECK: historical
          experiment: all-forcing simulation of the recent past
          experiment_id: historical
          external_variables: areacella
          forcing_index: 1
          frequency: mon
          further_info_url: https://furtherinfo.es-doc.org/CMIP6.BCC.BCC-ESM1.historical.none.r1i1...
          grid: T42
          grid_label: gn
          history: 2018-12-17T02:20:59Z altered by CMOR: Inverted axis: lev.
          initialization_index: 1
          institution: Beijing Climate Center, Beijing 100081, China
          institution_id: BCC
          license: CMIP6 model data produced by BCC is licensed under a Creative Commons Attribution...
          mip_era: CMIP6
          nominal_resolution: 250 km
          original_name: CLOUD
          parent_activity_id: CMIP
          parent_experiment_id: piControl
          parent_mip_era: CMIP6
          parent_source_id: BCC-ESM1
          parent_time_units: days since 1850-01-01
          parent_variant_label: r1i1p1f1
          physics_index: 1
          product: model-output
          realization_index: 1
          realm: atmos
          references: Model described by Tongwen Wu et al. (JGR 2013; JMR 2014; submmitted to...
          run_variant: forcing: greenhouse gases,aerosol emission,solar constant,volcano mass,land...
          source: BCC-ESM 1 (2017):   aerosol: none  atmos: BCC_AGCM3_LR (T42; 128 x 64 longitude/latitude;...
          source_id: BCC-ESM1
          source_type: AER AOGCM CHEM
          sub_experiment: none
          sub_experiment_id: none
          table_id: Amon
          table_info: Creation Date:(30 July 2018) MD5:e53ff52009d0b97d9d867dc12b6096c7
          title: BCC-ESM1 output prepared for CMIP6
          tracking_id: hdl:21.14100/6f5b873f-9609-4be8-99d6-bfde65468e67
          variable_id: cl
          variant_label: r1i1p1f1
     Cell methods:
          mean: area (20 minutes), time (20 minutes)

I’ve got one file, the other is almost there… so glad we did this as the file cl_Amon_BCC-ESM1_historical_r1i1p1f1_gn_185001-201412 contains the alternative formula_terms = "p0: p0 a: a b: b ps: ps" for a atmosphere_hybrid_sigma_pressure_coordinate, where ap (delta) is formed by performing p0 * a.

In the example file a has no units and p0 has units of Pa (which does and must match ps), however the p0 * a results in a delta with units of unknown and causes the same failure, but in a different way.

I’ve located the fix in iris/fileformats/netcdf.py before it then creates the aux factory, as it’s too late for the patch in the aux factory to fix this particular nuance.

Re-testing now…

Blimey it’s a sloooooow connection… 😴

@bjlittle You can just download the files directly from ESGF:

wget http://esgf3.dkrz.de/thredds/fileServer/cmip6/CMIP/BCC/BCC-ESM1/historical/r1i1p1f1/Amon/cl/gn/v20181217/cl_Amon_BCC-ESM1_historical_r1i1p1f1_gn_185001-201412.nc
wget http://esgf3.dkrz.de/thredds/fileServer/cmip6/CMIP/MOHC/UKESM1-0-LL/historical/r1i1p1f2/Amon/cl/gn/v20190406/cl_Amon_UKESM1-0-LL_historical_r1i1p1f2_gn_185001-189912.nc

I’ve put two of those files on JASMIN at /home/users/valeriu/badData which is open to all JASMIN users, unfortunately the local server that I use to get into JASMIN is read-only today (as it happens, when you need something done), and I can’t scp them anywhere w/o first going through it - any chance any of yous can grab them straight from my JASMIN location?

You can check cl of all CMIP6 models, I guess there’s more of them. I have a meeting now, can help you guys later! 👍

Thanks V.!

I found another one with hybrid height: MOHC/UKESM1-0-LL/historical/r1i1p1f2/Amon/cl/gn/v20190406/cl_Amon_UKESM1-0-LL_historical_r1i1p1f2_gn_185001-189912.nc.

told you they’re like ants 😆 That’s on the ESGF node (at DKRZ I assume, since Manu works on that one), let me try grab it off CEDA-JASMIN and put it somewhere where you can download it

No worries, that’s not a problem. Thanks for checking, much appreciated 👍

This has turned out to be such a simple change, I’m pretty confident that similar files with no units attributes on NetCDF variables that should have explicit units = 1 (dimensionless) will work (pride before a fall 🤕)

massive 👏 @bjlittle - unfortunately both @schlunma and me couldn’t find anymore of these messed files, but there is a good probability there may be out there, given that these are like ants - once you see one they’re bound to be more

cirrus-ci is certainly happy with the fix i.e., no side-effects that our testing coverage can pick up… that’s encouraging (or we have a hole in our test coverage 😆)

I’m just going to check our unit tests and ensure there is test coverage for at least this change… then we should be good to release.

@bjlittle Thanks, that sounds like a great solution!

@bjlittle hang on, I’m rerunning the tests, try find more files, if @schlunma doesn’t beat me to it 😃

@valeriupredoi Okay, no worries dude. Leave it with me and I’ll get back to you asap… just need to speak to the team about this +1

you’re legend @bjlittle cheers muchly! 🍺

@valeriupredoi Okay, no worries dude. Leave it with me and I’ll get back to you asap… just need to speak to the team about this 👍

@valeriupredoi Done a quick hack to see, if in principle, this promotion of units of unknown to dimensionless (1) within auxiliary factories works…

Python 3.7.8 | packaged by conda-forge | (default, Nov 17 2020, 23:45:15) 
Type 'copyright', 'credits' or 'license' for more information
IPython 7.12.0 -- An enhanced Interactive Python. Type '?' for help.
PyDev console: using IPython 7.12.0
Python 3.7.8 | packaged by conda-forge | (default, Nov 17 2020, 23:45:15) 
[GCC 7.5.0] on linux
>>> import iris
>>> iris.__version__
'3.0.0'
>>> fname = "/downloads/common_cl_ap.nc"
>>> cubes = iris.load(fname)
/net/home/h05/itwl/projects/git/iris/lib/iris/fileformats/cf.py:1207: UserWarning: Ignoring formula terms variable 'ps' referenced by data variable 'ap_bnds' via variable 'lev': Dimensions ('time', 'lat', 'lon') do not span ('lev', 'bnds')
  warnings.warn(msg)
/net/home/h05/itwl/projects/git/iris/lib/iris/fileformats/cf.py:1207: UserWarning: Ignoring formula terms variable 'ps' referenced by data variable 'b_bnds' via variable 'lev': Dimensions ('time', 'lat', 'lon') do not span ('lev', 'bnds')
  warnings.warn(msg)
/net/home/h05/itwl/projects/git/iris/lib/iris/fileformats/netcdf.py:685: UserWarning: Unable to find coordinate for variable 'ps'
  "{!r}".format(name)
/net/home/h05/itwl/projects/git/iris/lib/iris/fileformats/netcdf.py:685: UserWarning: Unable to find coordinate for variable 'ps'
  "{!r}".format(name)
>>> print(cubes)
0: ap_bnds / (unknown)                 (atmosphere_hybrid_sigma_pressure_coordinate: 2; -- : 2)
1: b_bnds / (unknown)                  (atmosphere_hybrid_sigma_pressure_coordinate: 2; -- : 2)
2: cloud_area_fraction_in_atmosphere_layer / (%) (time: 1; atmosphere_hybrid_sigma_pressure_coordinate: 2; latitude: 3; longitude: 4)
3: surface_air_pressure / (Pa)         (time: 1; latitude: 3; longitude: 4)
print(cubes[2])
cloud_area_fraction_in_atmosphere_layer    (time: 1; atmosphere_hybrid_sigma_pressure_coordinate: 2; latitude: 3; longitude: 4)
     Dimension coordinates:
          time                                            x                                               -            -             -
          atmosphere_hybrid_sigma_pressure_coordinate     -                                               x            -             -
          latitude                                        -                                               -            x             -
          longitude                                       -                                               -            -             x
     Auxiliary coordinates:
          surface_air_pressure                            x                                               -            x             x
          ap                                              -                                               x            -             -
          b                                               -                                               x            -             -
     Derived coordinates:
          air_pressure                                    x                                               x            x             x

seems possible