From 3b6f87faaf79c759d65730e249453d79f0e29440 Mon Sep 17 00:00:00 2001 From: sloosvel Date: Fri, 12 Jul 2019 11:18:36 +0200 Subject: [PATCH 01/47] First working version --- .../primavera/eady_growth_rate.py | 130 ++++++++++++++++++ .../recipes/recipe_eady_growth_rate.yml | 61 ++++++++ 2 files changed, 191 insertions(+) create mode 100644 esmvaltool/diag_scripts/primavera/eady_growth_rate.py create mode 100644 esmvaltool/recipes/recipe_eady_growth_rate.yml diff --git a/esmvaltool/diag_scripts/primavera/eady_growth_rate.py b/esmvaltool/diag_scripts/primavera/eady_growth_rate.py new file mode 100644 index 0000000000..19c436df12 --- /dev/null +++ b/esmvaltool/diag_scripts/primavera/eady_growth_rate.py @@ -0,0 +1,130 @@ +import os +import logging +import string + +import iris +import iris.cube +import iris.analysis +import iris.util + +import numpy as np + +import numba +from numba import guvectorize, float64 + + +from dask import array as da + +import esmvaltool.diag_scripts.shared +import esmvaltool.diag_scripts.shared.names as n +from esmvaltool.diag_scripts.shared import group_metadata + +logger = logging.getLogger(os.path.basename(__file__)) + + +class EadyGrowthRate(object): + def __init__(self, config): + self.cfg = config + self.filenames = esmvaltool.diag_scripts.shared.Datasets(self.cfg) + self.g = 9.80665 + self.con = 0.3098 + self.omega = 7.292e-5 + + def compute(self): + data = group_metadata(self.cfg['input_data'].values(), 'dataset') + for key in data: + + ta = iris.load_cube(data[key][0]['filename']) + plev = ta.dim_coords[1] + min_plev = plev.points.min() + + theta = self.potential_temperature(ta, plev) + + del ta + + zg = iris.load_cube(data[key][1]['filename']) + + brunt = self.brunt_vaisala_frq(theta, zg) + + del theta + + lats = zg.dim_coords[2] + fcor = self.coriolis(lats, zg.shape) + + ua = iris.load_cube(data[key][2]['filename']) + + egr = self.eady_growth_rate(fcor, ua, zg, brunt) + + + iris.save(iris.cube.Cube(egr), '/scratch/Earth/sloosvel/esmvaltool/output/brun.nc') + + + + + + + a=1 + + + def potential_temperature(self, ta, plev): + p0 = iris.coords.AuxCoord(1000.0, long_name='reference_pressure', + units='hPa') + p0.convert_units(plev.units) + p = (p0.points/plev.points)**(2/7) + theta = ta * iris.util.broadcast_to_shape(p, ta.shape, (1,)) + theta.long_name = 'potential_air_temperature' + return theta + + def vertical_integration(self, x, y): + # Weird attempt to perform a non-cyclic centered difference to integrate along pressure levels + plevs = x.shape[1] + dxdy_0 = (x[:, 1, :, :].lazy_data() - x[:, 0, :, :].lazy_data()) / (y[:, 1, :, :].lazy_data() - y[:, 0, :, :].lazy_data()) + + + dxdy_centre = (x[:, 2:plevs, :, :].lazy_data() - x[:, 0:plevs-2, :, :].lazy_data()) / (y[:, 2:plevs, :, :].lazy_data() - y[:, 0:plevs-2, :, :].lazy_data()) + dxdy_end = (x[:, plevs-1, :, :].lazy_data() - x[:, plevs-2, :, :].lazy_data()) / (y[:, plevs-1, :, :].lazy_data() - y[:, plevs-2, :, :].lazy_data()) + + bounds = [dxdy_end, dxdy_0] + stacked_bounds = da.stack(bounds, axis=1) + total = [dxdy_centre, stacked_bounds] + + dxdy = da.concatenate(total, axis=1) + + dxdy = da.roll(dxdy, 1, axis=1) + + return dxdy + + def brunt_vaisala_frq(self, theta, zg): + dthdz = self.vertical_integration(theta, zg) + dthdz = da.where(dthdz > 0, dthdz, 0) + buoy = (self.g / theta.lazy_data()) * dthdz + brunt = da.sqrt(buoy) + brunt = da.where(brunt != 0, brunt, 1e20) + + return brunt + + def coriolis(self, lats, ndim): + fcor = 2*self.omega*np.sin(np.radians(lats.points)) + fcor = fcor[np.newaxis, np.newaxis, :, np.newaxis] + fcor = da.broadcast_to(fcor, ndim) + + + return fcor + + def eady_growth_rate(self, fcor, ua, zg, brunt): + dudz = self.vertical_integration(ua, zg) + egr = self.con * abs(fcor) * abs(dudz) / brunt + + return egr + + + + + +def main(): + with esmvaltool.diag_scripts.shared.run_diagnostic() as config: + EadyGrowthRate(config).compute() + + +if __name__ == "__main__": + main() diff --git a/esmvaltool/recipes/recipe_eady_growth_rate.yml b/esmvaltool/recipes/recipe_eady_growth_rate.yml new file mode 100644 index 0000000000..edd1c7a16b --- /dev/null +++ b/esmvaltool/recipes/recipe_eady_growth_rate.yml @@ -0,0 +1,61 @@ +# ESMValTool +--- +documentation: + description: | + This is an example recipe for a personal diagnostic. + You can run any Python diagnostic of your choice + without installing ESMValTool as developer or git pushing. + Simply include the full path to your script of choice in script, + see example /path/to/your/my_little_diagnostic.py + An example personal diagnostic can be found in + esmvaltool/diag_scripts/examples/my_little_diagnostic.py + + authors: + - pred_va + + maintainer: + - pred_va + +datasets: + - {dataset: MPI-ESM-LR, project: CMIP5, exp: decadal1997, ensemble: r1i1p1, mip: Amon, start_year: 2000, end_year: 2002} + +preprocessors: + annual_mean: + annual_mean: +# summer_mean: +# seasonal_mean: 'JJA' +# winter_mean: +# seasonal_mean: 'DJF' + +diagnostics: + annual_EGR: + variables: + ta: + preprocessor: annual_mean + zg: + preprocessor: annual_mean + ua: + preprocessor: annual_mean + scripts: + annual_eady_growth_rate: + script: primavera/eady_growth_rate.py + + # summer_EGR: + # variables: + # ta: + # preprocessor: summer_mean + # zg: + # preprocessor: summer_mean + # ua: + # preprocessor: summer_mean + # script: primavera/eady_growth_rate.py + + # winter_EGR: + # variables: + # ta: + # preprocessor: winter_mean + # zg: + # preprocessor: winter_mean + # ua: + # preprocessor: winter_mean + # script: primavera/eady_growth_rate.py From b22adde5840421b53f7c926825bb97a460ad7344 Mon Sep 17 00:00:00 2001 From: sloosvel Date: Fri, 12 Jul 2019 16:29:48 +0200 Subject: [PATCH 02/47] Perform temporal mean --- .../primavera/eady_growth_rate.py | 71 +++++++++++-------- 1 file changed, 42 insertions(+), 29 deletions(-) diff --git a/esmvaltool/diag_scripts/primavera/eady_growth_rate.py b/esmvaltool/diag_scripts/primavera/eady_growth_rate.py index 19c436df12..0eb3dbd919 100644 --- a/esmvaltool/diag_scripts/primavera/eady_growth_rate.py +++ b/esmvaltool/diag_scripts/primavera/eady_growth_rate.py @@ -9,16 +9,14 @@ import numpy as np -import numba -from numba import guvectorize, float64 - - from dask import array as da import esmvaltool.diag_scripts.shared import esmvaltool.diag_scripts.shared.names as n from esmvaltool.diag_scripts.shared import group_metadata +from esmvalcore.preprocessor import _time + logger = logging.getLogger(os.path.basename(__file__)) @@ -26,48 +24,41 @@ class EadyGrowthRate(object): def __init__(self, config): self.cfg = config self.filenames = esmvaltool.diag_scripts.shared.Datasets(self.cfg) + self.fill_value = 1e20 + self.ref_p = 1000.0 self.g = 9.80665 self.con = 0.3098 self.omega = 7.292e-5 def compute(self): data = group_metadata(self.cfg['input_data'].values(), 'dataset') - for key in data: - - ta = iris.load_cube(data[key][0]['filename']) + for dataset in data: + ta = iris.load_cube(data[dataset][0]['filename']) plev = ta.dim_coords[1] - min_plev = plev.points.min() theta = self.potential_temperature(ta, plev) del ta - zg = iris.load_cube(data[key][1]['filename']) + zg = iris.load_cube(data[dataset][1]['filename']) - brunt = self.brunt_vaisala_frq(theta, zg) - - del theta + brunt = self.brunt_vaisala_frq(theta, zg) lats = zg.dim_coords[2] fcor = self.coriolis(lats, zg.shape) - ua = iris.load_cube(data[key][2]['filename']) + ua = iris.load_cube(data[dataset][2]['filename']) egr = self.eady_growth_rate(fcor, ua, zg, brunt) + cube_egr = ua.copy(egr * 86400) - iris.save(iris.cube.Cube(egr), '/scratch/Earth/sloosvel/esmvaltool/output/brun.nc') - - - - - - - a=1 + cube_egr = cube_egr.collapsed('time', iris.analysis.MEAN) + self.save(cube_egr, dataset) def potential_temperature(self, ta, plev): - p0 = iris.coords.AuxCoord(1000.0, long_name='reference_pressure', + p0 = iris.coords.AuxCoord(self.ref_p, long_name='reference_pressure', units='hPa') p0.convert_units(plev.units) p = (p0.points/plev.points)**(2/7) @@ -76,20 +67,32 @@ def potential_temperature(self, ta, plev): return theta def vertical_integration(self, x, y): - # Weird attempt to perform a non-cyclic centered difference to integrate along pressure levels + # Perform a non-cyclic centered finite-difference to integrate + # along pressure levels + plevs = x.shape[1] - dxdy_0 = (x[:, 1, :, :].lazy_data() - x[:, 0, :, :].lazy_data()) / (y[:, 1, :, :].lazy_data() - y[:, 0, :, :].lazy_data()) + dxdy_0 = ((x[:, 1, :, :].lazy_data() - x[:, 0, :, :].lazy_data()) / + (y[:, 1, :, :].lazy_data() - y[:, 0, :, :].lazy_data())) + + dxdy_centre = ((x[:, 2:plevs, :, :].lazy_data() - + x[:, 0:plevs-2, :, :].lazy_data()) / + (y[:, 2:plevs, :, :].lazy_data() - + y[:, 0:plevs-2, :, :].lazy_data())) - dxdy_centre = (x[:, 2:plevs, :, :].lazy_data() - x[:, 0:plevs-2, :, :].lazy_data()) / (y[:, 2:plevs, :, :].lazy_data() - y[:, 0:plevs-2, :, :].lazy_data()) - dxdy_end = (x[:, plevs-1, :, :].lazy_data() - x[:, plevs-2, :, :].lazy_data()) / (y[:, plevs-1, :, :].lazy_data() - y[:, plevs-2, :, :].lazy_data()) + dxdy_end = ((x[:, plevs-1, :, :].lazy_data() - + x[:, plevs-2, :, :].lazy_data()) / + (y[:, plevs-1, :, :].lazy_data() - + y[:, plevs-2, :, :].lazy_data())) bounds = [dxdy_end, dxdy_0] stacked_bounds = da.stack(bounds, axis=1) total = [dxdy_centre, stacked_bounds] + # Concatenate arrays where the last slice is dxdy_0 dxdy = da.concatenate(total, axis=1) + # Move dxdy_0 to the beggining of the array dxdy = da.roll(dxdy, 1, axis=1) return dxdy @@ -99,16 +102,15 @@ def brunt_vaisala_frq(self, theta, zg): dthdz = da.where(dthdz > 0, dthdz, 0) buoy = (self.g / theta.lazy_data()) * dthdz brunt = da.sqrt(buoy) - brunt = da.where(brunt != 0, brunt, 1e20) + brunt = da.where(brunt != 0, brunt, self.fill_value) return brunt def coriolis(self, lats, ndim): - fcor = 2*self.omega*np.sin(np.radians(lats.points)) + fcor = 2.0*self.omega*np.sin(np.radians(lats.points)) fcor = fcor[np.newaxis, np.newaxis, :, np.newaxis] fcor = da.broadcast_to(fcor, ndim) - return fcor def eady_growth_rate(self, fcor, ua, zg, brunt): @@ -117,8 +119,19 @@ def eady_growth_rate(self, fcor, ua, zg, brunt): return egr + def save(self, egr, dataset): + egr.standard_name = None + egr.long_name = 'eady_growth_rate' + egr.var_name = 'egr' + egr.units = 'day-1' + script = self.cfg[n.SCRIPT] + filename = '{dataset}_{script}.nc'.format( + dataset=dataset, + script=script + ) + iris.save(egr, os.path.join(self.cfg[n.WORK_DIR], filename)) def main(): From 41a832bbb9d430902e9071a2515902fd8f1ead7d Mon Sep 17 00:00:00 2001 From: sloosvel Date: Fri, 12 Jul 2019 16:32:17 +0200 Subject: [PATCH 03/47] Fix some lint issues --- esmvaltool/diag_scripts/primavera/eady_growth_rate.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/esmvaltool/diag_scripts/primavera/eady_growth_rate.py b/esmvaltool/diag_scripts/primavera/eady_growth_rate.py index 0eb3dbd919..4778ddc11d 100644 --- a/esmvaltool/diag_scripts/primavera/eady_growth_rate.py +++ b/esmvaltool/diag_scripts/primavera/eady_growth_rate.py @@ -15,7 +15,6 @@ import esmvaltool.diag_scripts.shared.names as n from esmvaltool.diag_scripts.shared import group_metadata -from esmvalcore.preprocessor import _time logger = logging.getLogger(os.path.basename(__file__)) @@ -76,14 +75,14 @@ def vertical_integration(self, x, y): (y[:, 1, :, :].lazy_data() - y[:, 0, :, :].lazy_data())) dxdy_centre = ((x[:, 2:plevs, :, :].lazy_data() - - x[:, 0:plevs-2, :, :].lazy_data()) / + x[:, 0:plevs-2, :, :].lazy_data()) / (y[:, 2:plevs, :, :].lazy_data() - - y[:, 0:plevs-2, :, :].lazy_data())) + y[:, 0:plevs-2, :, :].lazy_data())) dxdy_end = ((x[:, plevs-1, :, :].lazy_data() - - x[:, plevs-2, :, :].lazy_data()) / + x[:, plevs-2, :, :].lazy_data()) / (y[:, plevs-1, :, :].lazy_data() - - y[:, plevs-2, :, :].lazy_data())) + y[:, plevs-2, :, :].lazy_data())) bounds = [dxdy_end, dxdy_0] stacked_bounds = da.stack(bounds, axis=1) From 36c5444d6f3e9fdaa4700da4172e280f370d6ca7 Mon Sep 17 00:00:00 2001 From: sloosvel Date: Mon, 15 Jul 2019 12:38:39 +0200 Subject: [PATCH 04/47] Extract season properly --- .../recipes/recipe_eady_growth_rate.yml | 44 +++++++++++-------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/esmvaltool/recipes/recipe_eady_growth_rate.yml b/esmvaltool/recipes/recipe_eady_growth_rate.yml index edd1c7a16b..09b7fabacd 100644 --- a/esmvaltool/recipes/recipe_eady_growth_rate.yml +++ b/esmvaltool/recipes/recipe_eady_growth_rate.yml @@ -22,34 +22,40 @@ datasets: preprocessors: annual_mean: annual_mean: -# summer_mean: -# seasonal_mean: 'JJA' -# winter_mean: -# seasonal_mean: 'DJF' + summer_mean: + extract_season: + season: 'JJA' + seasonal_mean: + winter_mean: + extract_season: + season: 'DJF' + seasonal_mean: diagnostics: - annual_EGR: + # annual_EGR: + # variables: + # ta: + # preprocessor: annual_mean + # zg: + # preprocessor: annual_mean + # ua: + # preprocessor: annual_mean + # scripts: + # annual_eady_growth_rate: + # script: primavera/eady_growth_rate.py + + summer_EGR: variables: ta: - preprocessor: annual_mean + preprocessor: summer_mean zg: - preprocessor: annual_mean + preprocessor: summer_mean ua: - preprocessor: annual_mean + preprocessor: summer_mean scripts: - annual_eady_growth_rate: + summer_eady_growth_rate: script: primavera/eady_growth_rate.py - # summer_EGR: - # variables: - # ta: - # preprocessor: summer_mean - # zg: - # preprocessor: summer_mean - # ua: - # preprocessor: summer_mean - # script: primavera/eady_growth_rate.py - # winter_EGR: # variables: # ta: From 81e1e874e67014f23fdfb811b25d6f3888215b0f Mon Sep 17 00:00:00 2001 From: sloosvel Date: Mon, 15 Jul 2019 13:07:45 +0200 Subject: [PATCH 05/47] Add recipe documentation --- esmvaltool/config-references.yml | 13 +++- .../recipes/recipe_eady_growth_rate.yml | 60 +++++++++---------- 2 files changed, 41 insertions(+), 32 deletions(-) diff --git a/esmvaltool/config-references.yml b/esmvaltool/config-references.yml index f0558ed81c..56e458331d 100644 --- a/esmvaltool/config-references.yml +++ b/esmvaltool/config-references.yml @@ -218,6 +218,10 @@ authors: name: Lledó, Llorenç institute: BSC, Spain email: llorenç.lledo 'at' bsc.es + loos_sa: + name: Loosveldt-Tomas, Saskia + institute: BSC, Spain + email: saskia.loosveldt 'at' bsc.es lore_ru: name: Lorenz, Ruth institute: ETH Zurich, Switzerland @@ -300,6 +304,10 @@ authors: name: Roehrig, Romain institute: MeteoFr, France email: romain.roehrig 'at' meteo.fr + sanc_em: + name: Sanchez, Emilia + institute: CERFACS, France + email: emilia.sanchez 'at' cerfacs.fr senf_da: name: Senftleben, Daniel institute: DLR, Germany @@ -584,7 +592,7 @@ references: lloyd-hughes02jclim: "Lloyd-Hughes, B. and Saunders, M. A., Int. J. Climatol., 22, 1571-1592, doi:10.1002/joc.846, 2002." locarini10usgov: "Locarnini, R. A. et al., World Ocean Atlas 2009, Volume 1: Temperature. S. Levitus, Ed. NOAA Atlas NESDIS 68, U.S. Government Printing Office, Washington, D.C., 184 pp.,2010." lucarini14revgeop: "Lucarini et al., Rev. Geophys., 52, 809-859, doi:https://doi.org/10.1002/2013RG000446" - mehran14jgr: "Mehran, A. et al., J. Geophys. Res., 119, 4, 1695-1707, doi: 10.1002/2013JD021152, 2014." + mehran14jgr: "Mehran, A. et al., J. Geophys. Res., 119, 4, 1695-1707, doi: 10.1002/2013JD021152, 2014." manubens: "Manubens, N., et al., ENVIRON MODELL SOFTW 103, 29-42. doi:10.1016/j.envsoft.2018.01.018" mckee93: "McKee, T. B. and Doesken, N. J. and Kleist, J. In Proceedings of the 8th Conference on Applied Climatology, 17(22), 179-183, Boston, MA: American Meteorological Society, 1993." mueller14grl: "Mueller, B. and Seneviratne, S. I. Geophys. Res. Lett., 41, 128-134, doi:10.1002/2013GL058055, 2014." @@ -633,7 +641,7 @@ references: patmos-x: "Heidinger et al., NOAA National Centers for Environmental Information, doi:10.7289/V5348HCK, last access: 10 February 2019." woa: "Locarnini et al., World Ocean Atlas 2013, Vol. 1: Temperature, 2013." zhang-2011: "Zhang et al., WIREs Clim. Change, doi:10.1002/wcc.147, 2011" - sillman-2013: "Sillmann et al., J. Geophys. Res., doi:10.1029/2012JD018390, 2013" + sillman-2013: "Sillmann et al., J. Geophys. Res., doi:10.1029/2012JD018390, 2013" projects: c3s-magic: Copernicus Climate Change Service 34a Lot 2 (MAGIC) project @@ -647,6 +655,7 @@ projects: esmval: DLR project ESMVal eval4cmip: DLR and University of Bremen project funded by the Initiative and Networking Fund of the Helmholtz Society qa4ecv: QA4ECV + primavera: EU H2020 project PRIMAVERA trr181: DFG Project TRR-181 ukesm: UKESM, UK Earth System Model project (NERC) diff --git a/esmvaltool/recipes/recipe_eady_growth_rate.yml b/esmvaltool/recipes/recipe_eady_growth_rate.yml index 09b7fabacd..c6c2a8f573 100644 --- a/esmvaltool/recipes/recipe_eady_growth_rate.yml +++ b/esmvaltool/recipes/recipe_eady_growth_rate.yml @@ -2,22 +2,22 @@ --- documentation: description: | - This is an example recipe for a personal diagnostic. - You can run any Python diagnostic of your choice - without installing ESMValTool as developer or git pushing. - Simply include the full path to your script of choice in script, - see example /path/to/your/my_little_diagnostic.py - An example personal diagnostic can be found in - esmvaltool/diag_scripts/examples/my_little_diagnostic.py + Recipe to compute the annual, summer and winter Eady Growth Rates. The + output produces netcdf files for each model and season. + authors: - - pred_va + - sanc_em maintainer: - - pred_va + - loos_sa + + projects: + - primavera datasets: - - {dataset: MPI-ESM-LR, project: CMIP5, exp: decadal1997, ensemble: r1i1p1, mip: Amon, start_year: 2000, end_year: 2002} + - {dataset: CNRM-CM5, project: CMIP5, exp: historical, ensemble: r1i1p1, + mip: Amon, start_year: 1979, end_year: 2005} preprocessors: annual_mean: @@ -32,17 +32,17 @@ preprocessors: seasonal_mean: diagnostics: - # annual_EGR: - # variables: - # ta: - # preprocessor: annual_mean - # zg: - # preprocessor: annual_mean - # ua: - # preprocessor: annual_mean - # scripts: - # annual_eady_growth_rate: - # script: primavera/eady_growth_rate.py + annual_EGR: + variables: + ta: + preprocessor: annual_mean + zg: + preprocessor: annual_mean + ua: + preprocessor: annual_mean + scripts: + annual_eady_growth_rate: + script: primavera/eady_growth_rate.py summer_EGR: variables: @@ -56,12 +56,12 @@ diagnostics: summer_eady_growth_rate: script: primavera/eady_growth_rate.py - # winter_EGR: - # variables: - # ta: - # preprocessor: winter_mean - # zg: - # preprocessor: winter_mean - # ua: - # preprocessor: winter_mean - # script: primavera/eady_growth_rate.py + winter_EGR: + variables: + ta: + preprocessor: winter_mean + zg: + preprocessor: winter_mean + ua: + preprocessor: winter_mean + script: primavera/eady_growth_rate.py From 5ad70bae3cbde4cbffe819b2f071f048223d12be Mon Sep 17 00:00:00 2001 From: sloosvel Date: Mon, 12 Aug 2019 16:55:23 +0200 Subject: [PATCH 06/47] Load vars by shortname --- .../diag_scripts/primavera/eady_growth_rate.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/esmvaltool/diag_scripts/primavera/eady_growth_rate.py b/esmvaltool/diag_scripts/primavera/eady_growth_rate.py index 4778ddc11d..fb8c7fce05 100644 --- a/esmvaltool/diag_scripts/primavera/eady_growth_rate.py +++ b/esmvaltool/diag_scripts/primavera/eady_growth_rate.py @@ -30,23 +30,24 @@ def __init__(self, config): self.omega = 7.292e-5 def compute(self): - data = group_metadata(self.cfg['input_data'].values(), 'dataset') - for dataset in data: - ta = iris.load_cube(data[dataset][0]['filename']) + data = group_metadata(self.cfg['input_data'].values(), 'alias') + for alias in data: + var = group_metadata(data[alias], 'short_name') + ta = iris.load_cube(var['ta'][0]['filename']) plev = ta.dim_coords[1] theta = self.potential_temperature(ta, plev) del ta - zg = iris.load_cube(data[dataset][1]['filename']) + zg = iris.load_cube(var['zg'][0]['filename']) brunt = self.brunt_vaisala_frq(theta, zg) lats = zg.dim_coords[2] fcor = self.coriolis(lats, zg.shape) - ua = iris.load_cube(data[dataset][2]['filename']) + ua = iris.load_cube(var['ua'][0]['filename']) egr = self.eady_growth_rate(fcor, ua, zg, brunt) @@ -54,7 +55,7 @@ def compute(self): cube_egr = cube_egr.collapsed('time', iris.analysis.MEAN) - self.save(cube_egr, dataset) + self.save(cube_egr, alias, data) def potential_temperature(self, ta, plev): p0 = iris.coords.AuxCoord(self.ref_p, long_name='reference_pressure', @@ -118,13 +119,14 @@ def eady_growth_rate(self, fcor, ua, zg, brunt): return egr - def save(self, egr, dataset): + def save(self, egr, alias, data): egr.standard_name = None egr.long_name = 'eady_growth_rate' egr.var_name = 'egr' egr.units = 'day-1' script = self.cfg[n.SCRIPT] + dataset = data[alias][0]['dataset'] filename = '{dataset}_{script}.nc'.format( dataset=dataset, script=script From 4f4bb8c5c4ca3432fe53eb1de8c4173b7b473696 Mon Sep 17 00:00:00 2001 From: sloosvel Date: Wed, 28 Aug 2019 10:29:42 +0200 Subject: [PATCH 07/47] Fix winter script --- esmvaltool/recipes/recipe_eady_growth_rate.yml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/esmvaltool/recipes/recipe_eady_growth_rate.yml b/esmvaltool/recipes/recipe_eady_growth_rate.yml index c6c2a8f573..7ac315eb30 100644 --- a/esmvaltool/recipes/recipe_eady_growth_rate.yml +++ b/esmvaltool/recipes/recipe_eady_growth_rate.yml @@ -32,7 +32,7 @@ preprocessors: seasonal_mean: diagnostics: - annual_EGR: + annual_egr: variables: ta: preprocessor: annual_mean @@ -44,7 +44,7 @@ diagnostics: annual_eady_growth_rate: script: primavera/eady_growth_rate.py - summer_EGR: + summer_egr: variables: ta: preprocessor: summer_mean @@ -56,7 +56,7 @@ diagnostics: summer_eady_growth_rate: script: primavera/eady_growth_rate.py - winter_EGR: + winter_egr: variables: ta: preprocessor: winter_mean @@ -64,4 +64,6 @@ diagnostics: preprocessor: winter_mean ua: preprocessor: winter_mean - script: primavera/eady_growth_rate.py + scripts: + winter_eady_growth_rate: + script: primavera/eady_growth_rate.py From 5be048a33bb557b7d0b13a5fc68e23264e3962f8 Mon Sep 17 00:00:00 2001 From: sloosvel Date: Wed, 28 Aug 2019 12:01:36 +0200 Subject: [PATCH 08/47] Improve output file name --- .../primavera/eady_growth_rate.py | 35 ++++++++++++++++--- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/esmvaltool/diag_scripts/primavera/eady_growth_rate.py b/esmvaltool/diag_scripts/primavera/eady_growth_rate.py index fb8c7fce05..da6ff5bddf 100644 --- a/esmvaltool/diag_scripts/primavera/eady_growth_rate.py +++ b/esmvaltool/diag_scripts/primavera/eady_growth_rate.py @@ -14,6 +14,7 @@ import esmvaltool.diag_scripts.shared import esmvaltool.diag_scripts.shared.names as n from esmvaltool.diag_scripts.shared import group_metadata +# from esmvaltool.diag_scripts.shared._base import ProvenanceLogger logger = logging.getLogger(os.path.basename(__file__)) @@ -64,6 +65,7 @@ def potential_temperature(self, ta, plev): p = (p0.points/plev.points)**(2/7) theta = ta * iris.util.broadcast_to_shape(p, ta.shape, (1,)) theta.long_name = 'potential_air_temperature' + return theta def vertical_integration(self, x, y): @@ -107,7 +109,7 @@ def brunt_vaisala_frq(self, theta, zg): return brunt def coriolis(self, lats, ndim): - fcor = 2.0*self.omega*np.sin(np.radians(lats.points)) + fcor = 2.0 * self.omega * np.sin(np.radians(lats.points)) fcor = fcor[np.newaxis, np.newaxis, :, np.newaxis] fcor = da.broadcast_to(fcor, ndim) @@ -126,13 +128,38 @@ def save(self, egr, alias, data): egr.units = 'day-1' script = self.cfg[n.SCRIPT] + project = data[alias][0]['project'] dataset = data[alias][0]['dataset'] - filename = '{dataset}_{script}.nc'.format( + exp = data[alias][0]['exp'] + start = data[alias][0]['start_year'] + end = data[alias][0]['end_year'] + output = '{project}_{dataset}_{exp}_{script}_{start}_{end}.nc'.format( + project=project, dataset=dataset, - script=script + exp=exp, + script=script, + start=start, + end=end ) - iris.save(egr, os.path.join(self.cfg[n.WORK_DIR], filename)) + iris.save(egr, os.path.join(self.cfg[n.WORK_DIR], output)) + + # caption = ("{script} between {start} and {end}" + # "according to {dataset}").format( + # script=script.split('_'), + # start=start, + # end=end, + # dataset=dataset + # ) + + # record = { + # 'caption': caption, + # 'domains': ['global'], + # 'autors': ['sanc_em'], + # 'references': ['primavera'], + # 'ancestors': output + # } + # ProvenanceLogger(self.cfg).log(output, record) def main(): From 9339c6f40dc8c9ae053f94be7a0e05ba2b533950 Mon Sep 17 00:00:00 2001 From: sloosvel Date: Fri, 11 Oct 2019 16:23:15 +0200 Subject: [PATCH 09/47] Update preproc names --- esmvaltool/recipes/recipe_eady_growth_rate.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/esmvaltool/recipes/recipe_eady_growth_rate.yml b/esmvaltool/recipes/recipe_eady_growth_rate.yml index 7ac315eb30..d2e61480c3 100644 --- a/esmvaltool/recipes/recipe_eady_growth_rate.yml +++ b/esmvaltool/recipes/recipe_eady_growth_rate.yml @@ -21,15 +21,15 @@ datasets: preprocessors: annual_mean: - annual_mean: + annual_statistics: summer_mean: extract_season: season: 'JJA' - seasonal_mean: + seasonal_statistics: winter_mean: extract_season: season: 'DJF' - seasonal_mean: + seasonal_statistics: diagnostics: annual_egr: From e72f4df36b98f5f6d20ca071eb039d480e912c10 Mon Sep 17 00:00:00 2001 From: sloosvel Date: Mon, 14 Oct 2019 16:35:32 +0200 Subject: [PATCH 10/47] Update recipe --- esmvaltool/recipes/recipe_eady_growth_rate.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/esmvaltool/recipes/recipe_eady_growth_rate.yml b/esmvaltool/recipes/recipe_eady_growth_rate.yml index d2e61480c3..90f5760380 100644 --- a/esmvaltool/recipes/recipe_eady_growth_rate.yml +++ b/esmvaltool/recipes/recipe_eady_growth_rate.yml @@ -7,10 +7,10 @@ documentation: authors: - - sanc_em + - sanchez-gomez_emilia maintainer: - - loos_sa + - loosveldt-tomas_saskia projects: - primavera From a28c92679c28d07118e887c28b75491d8bd8400e Mon Sep 17 00:00:00 2001 From: sloosvel Date: Tue, 15 Oct 2019 11:12:33 +0200 Subject: [PATCH 11/47] Add provenance --- .../primavera/eady_growth_rate.py | 45 ++++++++++--------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/esmvaltool/diag_scripts/primavera/eady_growth_rate.py b/esmvaltool/diag_scripts/primavera/eady_growth_rate.py index da6ff5bddf..315c36881f 100644 --- a/esmvaltool/diag_scripts/primavera/eady_growth_rate.py +++ b/esmvaltool/diag_scripts/primavera/eady_growth_rate.py @@ -14,7 +14,7 @@ import esmvaltool.diag_scripts.shared import esmvaltool.diag_scripts.shared.names as n from esmvaltool.diag_scripts.shared import group_metadata -# from esmvaltool.diag_scripts.shared._base import ProvenanceLogger +from esmvaltool.diag_scripts.shared._base import ProvenanceLogger logger = logging.getLogger(os.path.basename(__file__)) @@ -35,7 +35,7 @@ def compute(self): for alias in data: var = group_metadata(data[alias], 'short_name') ta = iris.load_cube(var['ta'][0]['filename']) - plev = ta.dim_coords[1] + plev = ta.coord('air_pressure') theta = self.potential_temperature(ta, plev) @@ -63,7 +63,7 @@ def potential_temperature(self, ta, plev): units='hPa') p0.convert_units(plev.units) p = (p0.points/plev.points)**(2/7) - theta = ta * iris.util.broadcast_to_shape(p, ta.shape, (1,)) + theta = ta * iris.util.broadcast_to_shape(p, ta.shape, ta.coord_dims('air_pressure')) theta.long_name = 'potential_air_temperature' return theta @@ -133,7 +133,7 @@ def save(self, egr, alias, data): exp = data[alias][0]['exp'] start = data[alias][0]['start_year'] end = data[alias][0]['end_year'] - output = '{project}_{dataset}_{exp}_{script}_{start}_{end}.nc'.format( + output_name = '{project}_{dataset}_{exp}_{script}_{start}_{end}.nc'.format( project=project, dataset=dataset, exp=exp, @@ -141,25 +141,26 @@ def save(self, egr, alias, data): start=start, end=end ) + output_file = os.path.join(self.cfg[n.WORK_DIR], output_name) + iris.save(egr, output_file) + caption = ("{script} between {start} and {end}" + "according to {dataset}").format( + script=script.split('_'), + start=start, + end=end, + dataset=dataset + ) + ancestors = [data[alias][i]['filename'] for i in range(len(data[alias]))] + record = { + 'caption': caption, + 'domains': ['global'], + 'autors': ['sanchez-gomez_emilia'], + 'references': ['acknow_project'], + 'ancestors': ancestors + } + with ProvenanceLogger(self.cfg) as provenance_logger: + provenance_logger.log(output_file, record) - iris.save(egr, os.path.join(self.cfg[n.WORK_DIR], output)) - - # caption = ("{script} between {start} and {end}" - # "according to {dataset}").format( - # script=script.split('_'), - # start=start, - # end=end, - # dataset=dataset - # ) - - # record = { - # 'caption': caption, - # 'domains': ['global'], - # 'autors': ['sanc_em'], - # 'references': ['primavera'], - # 'ancestors': output - # } - # ProvenanceLogger(self.cfg).log(output, record) def main(): From cf00614b7b0d8777c9284a1fc338130c6e228378 Mon Sep 17 00:00:00 2001 From: sloosvel Date: Tue, 15 Oct 2019 12:42:54 +0200 Subject: [PATCH 12/47] Fix lint issues --- .../primavera/eady_growth_rate.py | 32 ++++++++++++------- 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/esmvaltool/diag_scripts/primavera/eady_growth_rate.py b/esmvaltool/diag_scripts/primavera/eady_growth_rate.py index 315c36881f..2f833a655a 100644 --- a/esmvaltool/diag_scripts/primavera/eady_growth_rate.py +++ b/esmvaltool/diag_scripts/primavera/eady_growth_rate.py @@ -1,6 +1,5 @@ import os import logging -import string import iris import iris.cube @@ -59,11 +58,14 @@ def compute(self): self.save(cube_egr, alias, data) def potential_temperature(self, ta, plev): - p0 = iris.coords.AuxCoord(self.ref_p, long_name='reference_pressure', + p0 = iris.coords.AuxCoord(self.ref_p, + long_name='reference_pressure', units='hPa') p0.convert_units(plev.units) p = (p0.points/plev.points)**(2/7) - theta = ta * iris.util.broadcast_to_shape(p, ta.shape, ta.coord_dims('air_pressure')) + theta = ta * iris.util.broadcast_to_shape(p, + ta.shape, + ta.coord_dims('air_pressure')) theta.long_name = 'potential_air_temperature' return theta @@ -133,16 +135,20 @@ def save(self, egr, alias, data): exp = data[alias][0]['exp'] start = data[alias][0]['start_year'] end = data[alias][0]['end_year'] - output_name = '{project}_{dataset}_{exp}_{script}_{start}_{end}.nc'.format( - project=project, - dataset=dataset, - exp=exp, - script=script, - start=start, - end=end - ) + output_name = '{project}_' + '{dataset}_' + '{exp}_' + '{script}_' + '{start}_' + '{end}.nc'.format(project=project, + dataset=dataset, + exp=exp, + script=script, + start=start, + end=end) output_file = os.path.join(self.cfg[n.WORK_DIR], output_name) iris.save(egr, output_file) + caption = ("{script} between {start} and {end}" "according to {dataset}").format( script=script.split('_'), @@ -150,7 +156,9 @@ def save(self, egr, alias, data): end=end, dataset=dataset ) - ancestors = [data[alias][i]['filename'] for i in range(len(data[alias]))] + for i in range(len(data[alias])): + ancestors = [data[alias][i]['filename']] + record = { 'caption': caption, 'domains': ['global'], From ee46ae3b43d4d02bc83bf87234dec5baed07e6f8 Mon Sep 17 00:00:00 2001 From: sloosvel Date: Wed, 16 Oct 2019 11:00:33 +0200 Subject: [PATCH 13/47] Fix identation --- .../diag_scripts/primavera/eady_growth_rate.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/esmvaltool/diag_scripts/primavera/eady_growth_rate.py b/esmvaltool/diag_scripts/primavera/eady_growth_rate.py index 2f833a655a..10b50a71d9 100644 --- a/esmvaltool/diag_scripts/primavera/eady_growth_rate.py +++ b/esmvaltool/diag_scripts/primavera/eady_growth_rate.py @@ -44,7 +44,7 @@ def compute(self): brunt = self.brunt_vaisala_frq(theta, zg) - lats = zg.dim_coords[2] + lats = zg.coord('latitude') fcor = self.coriolis(lats, zg.shape) ua = iris.load_cube(var['ua'][0]['filename']) @@ -135,11 +135,11 @@ def save(self, egr, alias, data): exp = data[alias][0]['exp'] start = data[alias][0]['start_year'] end = data[alias][0]['end_year'] - output_name = '{project}_' - '{dataset}_' - '{exp}_' - '{script}_' - '{start}_' + output_name = '{project}_' \ + '{dataset}_' \ + '{exp}_' \ + '{script}_' \ + '{start}_' \ '{end}.nc'.format(project=project, dataset=dataset, exp=exp, From b2ffc0b1e6f48625237cf43baa17c3cc14068b23 Mon Sep 17 00:00:00 2001 From: sloosvel Date: Wed, 16 Oct 2019 12:31:25 +0200 Subject: [PATCH 14/47] Fix lint issues --- esmvaltool/diag_scripts/primavera/eady_growth_rate.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/esmvaltool/diag_scripts/primavera/eady_growth_rate.py b/esmvaltool/diag_scripts/primavera/eady_growth_rate.py index 10b50a71d9..30b6f33027 100644 --- a/esmvaltool/diag_scripts/primavera/eady_growth_rate.py +++ b/esmvaltool/diag_scripts/primavera/eady_growth_rate.py @@ -58,14 +58,16 @@ def compute(self): self.save(cube_egr, alias, data) def potential_temperature(self, ta, plev): - p0 = iris.coords.AuxCoord(self.ref_p, + p0 = iris.coords.AuxCoord(self.ref_p, long_name='reference_pressure', units='hPa') p0.convert_units(plev.units) p = (p0.points/plev.points)**(2/7) - theta = ta * iris.util.broadcast_to_shape(p, - ta.shape, - ta.coord_dims('air_pressure')) + theta = ta * iris.util.broadcast_to_shape( + p, + ta.shape, + ta.coord_dims('air_pressure') + ) theta.long_name = 'potential_air_temperature' return theta @@ -170,7 +172,6 @@ def save(self, egr, alias, data): provenance_logger.log(output_file, record) - def main(): with esmvaltool.diag_scripts.shared.run_diagnostic() as config: EadyGrowthRate(config).compute() From e02c40627425ae822fabd865c4d17e12f5a69a64 Mon Sep 17 00:00:00 2001 From: sloosvel Date: Fri, 18 Oct 2019 09:51:08 +0200 Subject: [PATCH 15/47] Fix identation --- esmvaltool/diag_scripts/primavera/eady_growth_rate.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/esmvaltool/diag_scripts/primavera/eady_growth_rate.py b/esmvaltool/diag_scripts/primavera/eady_growth_rate.py index 30b6f33027..b2eda7ec96 100644 --- a/esmvaltool/diag_scripts/primavera/eady_growth_rate.py +++ b/esmvaltool/diag_scripts/primavera/eady_growth_rate.py @@ -64,10 +64,10 @@ def potential_temperature(self, ta, plev): p0.convert_units(plev.units) p = (p0.points/plev.points)**(2/7) theta = ta * iris.util.broadcast_to_shape( - p, - ta.shape, - ta.coord_dims('air_pressure') - ) + p, + ta.shape, + ta.coord_dims('air_pressure') + ) theta.long_name = 'potential_air_temperature' return theta @@ -167,7 +167,7 @@ def save(self, egr, alias, data): 'autors': ['sanchez-gomez_emilia'], 'references': ['acknow_project'], 'ancestors': ancestors - } + } with ProvenanceLogger(self.cfg) as provenance_logger: provenance_logger.log(output_file, record) From 24c2e27b0dfb45e56e4afd302d4932acb0df202c Mon Sep 17 00:00:00 2001 From: sloosvel <45196700+sloosvel@users.noreply.github.com> Date: Fri, 18 Oct 2019 10:20:17 +0200 Subject: [PATCH 16/47] Update esmvaltool/diag_scripts/primavera/eady_growth_rate.py Co-Authored-By: Bouwe Andela --- esmvaltool/diag_scripts/primavera/eady_growth_rate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esmvaltool/diag_scripts/primavera/eady_growth_rate.py b/esmvaltool/diag_scripts/primavera/eady_growth_rate.py index b2eda7ec96..8e6b5c7600 100644 --- a/esmvaltool/diag_scripts/primavera/eady_growth_rate.py +++ b/esmvaltool/diag_scripts/primavera/eady_growth_rate.py @@ -13,7 +13,7 @@ import esmvaltool.diag_scripts.shared import esmvaltool.diag_scripts.shared.names as n from esmvaltool.diag_scripts.shared import group_metadata -from esmvaltool.diag_scripts.shared._base import ProvenanceLogger +from esmvaltool.diag_scripts.shared import ProvenanceLogger logger = logging.getLogger(os.path.basename(__file__)) From e3c469324c789b8315b91c84ba1ed5734a64d110 Mon Sep 17 00:00:00 2001 From: sloosvel Date: Fri, 18 Oct 2019 17:46:22 +0200 Subject: [PATCH 17/47] Update recipe with PRIMAVERA datasets --- esmvaltool/recipes/recipe_eady_growth_rate.yml | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/esmvaltool/recipes/recipe_eady_growth_rate.yml b/esmvaltool/recipes/recipe_eady_growth_rate.yml index 90f5760380..c78957bec9 100644 --- a/esmvaltool/recipes/recipe_eady_growth_rate.yml +++ b/esmvaltool/recipes/recipe_eady_growth_rate.yml @@ -16,8 +16,17 @@ documentation: - primavera datasets: - - {dataset: CNRM-CM5, project: CMIP5, exp: historical, ensemble: r1i1p1, - mip: Amon, start_year: 1979, end_year: 2005} + - {dataset: CMCC-CM2-HR4, project: CMIP6, exp: highresSST-present, ensemble: r1i1p1f1, mip: Amon, grid: gn,start_year: 1950, end_year: 2014} + - {dataset: CNRM-CM6-1, project: CMIP6, exp: highresSST-present, ensemble: r1i1p1f2, mip: Amon, grid: gr, start_year: 1950, end_year: 2014} + - {dataset: CNRM-CM6-1-HR, project: CMIP6, exp: highresSST-present, ensemble: r1i1p1f2, mip: Amon, grid: gr, start_year: 1950, end_year: 2014} + - {dataset: ECMWF-IFS-HR, project: CMIP6, exp: highresSST-present, ensemble: r1i1p1f1, mip: Amon, grid: gr, start_year: 1950, end_year: 2014} + - {dataset: ECMWF-IFS-LR, project: CMIP6, exp: highresSST-present, ensemble: r1i1p1f1, mip: Amon, grid: gr, start_year: 1950, end_year: 2014} + - {dataset: HadGEM3-GC31-HM, project: CMIP6, exp: highresSST-present, ensemble: r1i1p1f1, mip: Amon, grid: gn, start_year: 1950, end_year: 2014} + - {dataset: HadGEM3-GC31-LM, project: CMIP6, exp: highresSST-present, ensemble: r1i1p1f1, mip: Amon, grid: gn, start_year: 1950, end_year: 2014} + - {dataset: HadGEM3-GC31-MM, project: CMIP6, exp: highresSST-present, ensemble: r1i1p1f1, mip: Amon, grid: gn, start_year: 1950, end_year: 2014} + - {dataset: MPI-ESM1-2-XR, institute: MPI-M, project: CMIP6, exp: highresSST-present, ensemble: r1i1p1f1, mip: Amon, grid: gn, start_year: 1950, end_year: 2014} + - {dataset: MPI-ESM1-2-HR, institute: MPI-M, project: CMIP6, exp: highresSST-present, ensemble: r1i1p1f1, mip: Amon, grid: gn, start_year: 1950, end_year: 2014} + preprocessors: annual_mean: From 37cdb166c601bd6fcc975f55405943f843803b59 Mon Sep 17 00:00:00 2001 From: sloosvel Date: Fri, 18 Oct 2019 17:48:24 +0200 Subject: [PATCH 18/47] Consider models that need ua to be regridded --- esmvaltool/diag_scripts/primavera/eady_growth_rate.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/esmvaltool/diag_scripts/primavera/eady_growth_rate.py b/esmvaltool/diag_scripts/primavera/eady_growth_rate.py index 8e6b5c7600..ba7fef49e8 100644 --- a/esmvaltool/diag_scripts/primavera/eady_growth_rate.py +++ b/esmvaltool/diag_scripts/primavera/eady_growth_rate.py @@ -10,6 +10,8 @@ from dask import array as da +from esmvalcore.preprocessor import regrid + import esmvaltool.diag_scripts.shared import esmvaltool.diag_scripts.shared.names as n from esmvaltool.diag_scripts.shared import group_metadata @@ -48,6 +50,8 @@ def compute(self): fcor = self.coriolis(lats, zg.shape) ua = iris.load_cube(var['ua'][0]['filename']) + if ua.shape is not zg.shape: + ua = regrid(ua, zg, scheme='linear') egr = self.eady_growth_rate(fcor, ua, zg, brunt) From ddf46fdc00bab2b6e7d0c109224520348223587f Mon Sep 17 00:00:00 2001 From: sloosvel Date: Fri, 18 Oct 2019 18:01:44 +0200 Subject: [PATCH 19/47] Update references properly --- esmvaltool/config-references.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/esmvaltool/config-references.yml b/esmvaltool/config-references.yml index e420886e7c..0ebe790076 100644 --- a/esmvaltool/config-references.yml +++ b/esmvaltool/config-references.yml @@ -245,8 +245,7 @@ authors: lledo_llorenc: name: Lledo, Llorenc institute: BSC, Spain - orcid: - email: + orcid: loosveldt-tomas_saskia: name: Loosveldt-Tomas, Saskia institute: BSC, Spain @@ -712,8 +711,8 @@ projects: esmval: DLR project ESMVal eval4cmip: DLR and University of Bremen project funded by the Initiative and Networking Fund of the Helmholtz Society ewatercycle: eWaterCycle project - qa4ecv: QA4ECV primavera: EU H2020 project PRIMAVERA + qa4ecv: QA4ECV trr181: DFG Project TRR-181 ukesm: UKESM, UK Earth System Model project (NERC) From 0c6d5092823862da8c736aa37805545c3b814d20 Mon Sep 17 00:00:00 2001 From: sloosvel Date: Wed, 23 Oct 2019 15:13:18 +0200 Subject: [PATCH 20/47] Fix yamllint issues --- .../recipes/recipe_eady_growth_rate.yml | 32 +++++++++++++------ 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/esmvaltool/recipes/recipe_eady_growth_rate.yml b/esmvaltool/recipes/recipe_eady_growth_rate.yml index c78957bec9..91a37421ce 100644 --- a/esmvaltool/recipes/recipe_eady_growth_rate.yml +++ b/esmvaltool/recipes/recipe_eady_growth_rate.yml @@ -16,16 +16,28 @@ documentation: - primavera datasets: - - {dataset: CMCC-CM2-HR4, project: CMIP6, exp: highresSST-present, ensemble: r1i1p1f1, mip: Amon, grid: gn,start_year: 1950, end_year: 2014} - - {dataset: CNRM-CM6-1, project: CMIP6, exp: highresSST-present, ensemble: r1i1p1f2, mip: Amon, grid: gr, start_year: 1950, end_year: 2014} - - {dataset: CNRM-CM6-1-HR, project: CMIP6, exp: highresSST-present, ensemble: r1i1p1f2, mip: Amon, grid: gr, start_year: 1950, end_year: 2014} - - {dataset: ECMWF-IFS-HR, project: CMIP6, exp: highresSST-present, ensemble: r1i1p1f1, mip: Amon, grid: gr, start_year: 1950, end_year: 2014} - - {dataset: ECMWF-IFS-LR, project: CMIP6, exp: highresSST-present, ensemble: r1i1p1f1, mip: Amon, grid: gr, start_year: 1950, end_year: 2014} - - {dataset: HadGEM3-GC31-HM, project: CMIP6, exp: highresSST-present, ensemble: r1i1p1f1, mip: Amon, grid: gn, start_year: 1950, end_year: 2014} - - {dataset: HadGEM3-GC31-LM, project: CMIP6, exp: highresSST-present, ensemble: r1i1p1f1, mip: Amon, grid: gn, start_year: 1950, end_year: 2014} - - {dataset: HadGEM3-GC31-MM, project: CMIP6, exp: highresSST-present, ensemble: r1i1p1f1, mip: Amon, grid: gn, start_year: 1950, end_year: 2014} - - {dataset: MPI-ESM1-2-XR, institute: MPI-M, project: CMIP6, exp: highresSST-present, ensemble: r1i1p1f1, mip: Amon, grid: gn, start_year: 1950, end_year: 2014} - - {dataset: MPI-ESM1-2-HR, institute: MPI-M, project: CMIP6, exp: highresSST-present, ensemble: r1i1p1f1, mip: Amon, grid: gn, start_year: 1950, end_year: 2014} + - {dataset: CMCC-CM2-HR4, project: CMIP6, exp: highresSST-present, + ensemble: r1i1p1f1, mip: Amon, grid: gn, start_year: 1950, end_year: 2014} + - {dataset: CNRM-CM6-1, project: CMIP6, exp: highresSST-present, + ensemble: r1i1p1f2, mip: Amon, grid: gr, start_year: 1950, end_year: 2014} + - {dataset: CNRM-CM6-1-HR, project: CMIP6, exp: highresSST-present, + ensemble: r1i1p1f2, mip: Amon, grid: gr, start_year: 1950, end_year: 2014} + - {dataset: ECMWF-IFS-HR, project: CMIP6, exp: highresSST-present, + ensemble: r1i1p1f1, mip: Amon, grid: gr, start_year: 1950, end_year: 2014} + - {dataset: ECMWF-IFS-LR, project: CMIP6, exp: highresSST-present, + ensemble: r1i1p1f1, mip: Amon, grid: gr, start_year: 1950, end_year: 2014} + - {dataset: HadGEM3-GC31-HM, project: CMIP6, exp: highresSST-present, + ensemble: r1i1p1f1, mip: Amon, grid: gn, start_year: 1950, end_year: 2014} + - {dataset: HadGEM3-GC31-LM, project: CMIP6, exp: highresSST-present, + ensemble: r1i1p1f1, mip: Amon, grid: gn, start_year: 1950, end_year: 2014} + - {dataset: HadGEM3-GC31-MM, project: CMIP6, exp: highresSST-present, + ensemble: r1i1p1f1, mip: Amon, grid: gn, start_year: 1950, end_year: 2014} + - {dataset: MPI-ESM1-2-XR, institute: MPI-M, project: CMIP6, + exp: highresSST-present, ensemble: r1i1p1f1, mip: Amon, grid: gn, + start_year: 1950, end_year: 2014} + - {dataset: MPI-ESM1-2-HR, institute: MPI-M, project: CMIP6, + exp: highresSST-present, ensemble: r1i1p1f1, mip: Amon, grid: gn, + start_year: 1950, end_year: 2014} preprocessors: From 4d4dd0c89f8910bf6cdc4b37d336a3dbc72058cf Mon Sep 17 00:00:00 2001 From: sloosvel Date: Thu, 7 Nov 2019 17:01:28 +0100 Subject: [PATCH 21/47] Remove institute --- esmvaltool/recipes/recipe_eady_growth_rate.yml | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/esmvaltool/recipes/recipe_eady_growth_rate.yml b/esmvaltool/recipes/recipe_eady_growth_rate.yml index 91a37421ce..5e485be25e 100644 --- a/esmvaltool/recipes/recipe_eady_growth_rate.yml +++ b/esmvaltool/recipes/recipe_eady_growth_rate.yml @@ -32,12 +32,10 @@ datasets: ensemble: r1i1p1f1, mip: Amon, grid: gn, start_year: 1950, end_year: 2014} - {dataset: HadGEM3-GC31-MM, project: CMIP6, exp: highresSST-present, ensemble: r1i1p1f1, mip: Amon, grid: gn, start_year: 1950, end_year: 2014} - - {dataset: MPI-ESM1-2-XR, institute: MPI-M, project: CMIP6, - exp: highresSST-present, ensemble: r1i1p1f1, mip: Amon, grid: gn, - start_year: 1950, end_year: 2014} - - {dataset: MPI-ESM1-2-HR, institute: MPI-M, project: CMIP6, - exp: highresSST-present, ensemble: r1i1p1f1, mip: Amon, grid: gn, - start_year: 1950, end_year: 2014} + - {dataset: MPI-ESM1-2-XR, project: CMIP6, exp: highresSST-present, + ensemble: r1i1p1f1, mip: Amon, grid: gn, start_year: 1950, end_year: 2014} + - {dataset: MPI-ESM1-2-HR, project: CMIP6, exp: highresSST-present, + ensemble: r1i1p1f1, mip: Amon, grid: gn, start_year: 1950, end_year: 2014} preprocessors: From dae04f40f710d68ab622298a2855ba30cc09a040 Mon Sep 17 00:00:00 2001 From: sloosvel Date: Tue, 19 Nov 2019 17:37:37 +0100 Subject: [PATCH 22/47] Reorder files --- .../primavera/{ => eady_growth_rate}/eady_growth_rate.py | 0 esmvaltool/recipes/recipe_eady_growth_rate.yml | 6 +++--- 2 files changed, 3 insertions(+), 3 deletions(-) rename esmvaltool/diag_scripts/primavera/{ => eady_growth_rate}/eady_growth_rate.py (100%) diff --git a/esmvaltool/diag_scripts/primavera/eady_growth_rate.py b/esmvaltool/diag_scripts/primavera/eady_growth_rate/eady_growth_rate.py similarity index 100% rename from esmvaltool/diag_scripts/primavera/eady_growth_rate.py rename to esmvaltool/diag_scripts/primavera/eady_growth_rate/eady_growth_rate.py diff --git a/esmvaltool/recipes/recipe_eady_growth_rate.yml b/esmvaltool/recipes/recipe_eady_growth_rate.yml index 5e485be25e..8a88633d14 100644 --- a/esmvaltool/recipes/recipe_eady_growth_rate.yml +++ b/esmvaltool/recipes/recipe_eady_growth_rate.yml @@ -61,7 +61,7 @@ diagnostics: preprocessor: annual_mean scripts: annual_eady_growth_rate: - script: primavera/eady_growth_rate.py + script: primavera/eady_growth_rate/eady_growth_rate.py summer_egr: variables: @@ -73,7 +73,7 @@ diagnostics: preprocessor: summer_mean scripts: summer_eady_growth_rate: - script: primavera/eady_growth_rate.py + script: primavera/eady_growth_rate/eady_growth_rate.py winter_egr: variables: @@ -85,4 +85,4 @@ diagnostics: preprocessor: winter_mean scripts: winter_eady_growth_rate: - script: primavera/eady_growth_rate.py + script: primavera/eady_growth_rate/eady_growth_rate.py From 1bdbf78fd2ca2de287822bbae6bac4f195e7695b Mon Sep 17 00:00:00 2001 From: sloosvel Date: Wed, 20 Nov 2019 15:07:31 +0100 Subject: [PATCH 23/47] Fix provenance --- .../primavera/eady_growth_rate/eady_growth_rate.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/esmvaltool/diag_scripts/primavera/eady_growth_rate/eady_growth_rate.py b/esmvaltool/diag_scripts/primavera/eady_growth_rate/eady_growth_rate.py index ba7fef49e8..621e7249c6 100644 --- a/esmvaltool/diag_scripts/primavera/eady_growth_rate/eady_growth_rate.py +++ b/esmvaltool/diag_scripts/primavera/eady_growth_rate/eady_growth_rate.py @@ -162,9 +162,9 @@ def save(self, egr, alias, data): end=end, dataset=dataset ) + ancestors = [] for i in range(len(data[alias])): - ancestors = [data[alias][i]['filename']] - + ancestors.append(data[alias][i]['filename']) record = { 'caption': caption, 'domains': ['global'], From 777a7bc22e1c3b90909733060d321c3f460573bc Mon Sep 17 00:00:00 2001 From: sloosvel Date: Thu, 20 Feb 2020 13:26:25 +0100 Subject: [PATCH 24/47] Compute climatology as an option --- .../primavera/eady_growth_rate/eady_growth_rate.py | 5 +++-- esmvaltool/recipes/recipe_eady_growth_rate.yml | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/esmvaltool/diag_scripts/primavera/eady_growth_rate/eady_growth_rate.py b/esmvaltool/diag_scripts/primavera/eady_growth_rate/eady_growth_rate.py index 621e7249c6..f1d1ace567 100644 --- a/esmvaltool/diag_scripts/primavera/eady_growth_rate/eady_growth_rate.py +++ b/esmvaltool/diag_scripts/primavera/eady_growth_rate/eady_growth_rate.py @@ -30,6 +30,7 @@ def __init__(self, config): self.g = 9.80665 self.con = 0.3098 self.omega = 7.292e-5 + self.compute_climatology = self.cfg['compute_climatology'] def compute(self): data = group_metadata(self.cfg['input_data'].values(), 'alias') @@ -56,8 +57,8 @@ def compute(self): egr = self.eady_growth_rate(fcor, ua, zg, brunt) cube_egr = ua.copy(egr * 86400) - - cube_egr = cube_egr.collapsed('time', iris.analysis.MEAN) + if self.compute_climatology: + cube_egr = cube_egr.collapsed('time', iris.analysis.MEAN) self.save(cube_egr, alias, data) diff --git a/esmvaltool/recipes/recipe_eady_growth_rate.yml b/esmvaltool/recipes/recipe_eady_growth_rate.yml index 8a88633d14..1adbf62ff7 100644 --- a/esmvaltool/recipes/recipe_eady_growth_rate.yml +++ b/esmvaltool/recipes/recipe_eady_growth_rate.yml @@ -86,3 +86,4 @@ diagnostics: scripts: winter_eady_growth_rate: script: primavera/eady_growth_rate/eady_growth_rate.py + compute_climatology: True From f7b1a7457d1fa40393ee3ee3095533e86785eae2 Mon Sep 17 00:00:00 2001 From: sloosvel Date: Thu, 20 Feb 2020 17:30:29 +0100 Subject: [PATCH 25/47] Add option to compute means at the end --- .../primavera/eady_growth_rate/eady_growth_rate.py | 11 ++++++++++- esmvaltool/recipes/recipe_eady_growth_rate.yml | 11 ++++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/esmvaltool/diag_scripts/primavera/eady_growth_rate/eady_growth_rate.py b/esmvaltool/diag_scripts/primavera/eady_growth_rate/eady_growth_rate.py index f1d1ace567..02ee60d817 100644 --- a/esmvaltool/diag_scripts/primavera/eady_growth_rate/eady_growth_rate.py +++ b/esmvaltool/diag_scripts/primavera/eady_growth_rate/eady_growth_rate.py @@ -10,7 +10,8 @@ from dask import array as da -from esmvalcore.preprocessor import regrid +from esmvalcore.preprocessor import ( + regrid, annual_statistics, seasonal_statistics) import esmvaltool.diag_scripts.shared import esmvaltool.diag_scripts.shared.names as n @@ -31,6 +32,8 @@ def __init__(self, config): self.con = 0.3098 self.omega = 7.292e-5 self.compute_climatology = self.cfg['compute_climatology'] + self.compute_annual_mean = self.cfg['compute_annual_mean'] + self.compute_seasonal_mean = self.cfg['compute_seasonal_mean'] def compute(self): data = group_metadata(self.cfg['input_data'].values(), 'alias') @@ -57,6 +60,12 @@ def compute(self): egr = self.eady_growth_rate(fcor, ua, zg, brunt) cube_egr = ua.copy(egr * 86400) + + if self.compute_annual_mean: + cube_egr = annual_statistics(cube_egr) + elif self.compute_seasonal_mean: + cube_egr = seasonal_statistics(cube_egr) + if self.compute_climatology: cube_egr = cube_egr.collapsed('time', iris.analysis.MEAN) diff --git a/esmvaltool/recipes/recipe_eady_growth_rate.yml b/esmvaltool/recipes/recipe_eady_growth_rate.yml index 1adbf62ff7..5bea4a4544 100644 --- a/esmvaltool/recipes/recipe_eady_growth_rate.yml +++ b/esmvaltool/recipes/recipe_eady_growth_rate.yml @@ -62,6 +62,10 @@ diagnostics: scripts: annual_eady_growth_rate: script: primavera/eady_growth_rate/eady_growth_rate.py + compute_annual_mean: false + compute_seasonal_mean: false + compute_climatology: true + summer_egr: variables: @@ -74,6 +78,9 @@ diagnostics: scripts: summer_eady_growth_rate: script: primavera/eady_growth_rate/eady_growth_rate.py + compute_annual_mean: false + compute_seasonal_mean: false + compute_climatology: true winter_egr: variables: @@ -86,4 +93,6 @@ diagnostics: scripts: winter_eady_growth_rate: script: primavera/eady_growth_rate/eady_growth_rate.py - compute_climatology: True + compute_annual_mean: false + compute_seasonal_mean: false + compute_climatology: true From 895d26cc5988499a49b4ff6bf541b31b697c8658 Mon Sep 17 00:00:00 2001 From: sloosvel Date: Fri, 21 Feb 2020 15:42:16 +0100 Subject: [PATCH 26/47] Fix style issues --- .../eady_growth_rate/eady_growth_rate.py | 27 ++++++------------- 1 file changed, 8 insertions(+), 19 deletions(-) diff --git a/esmvaltool/diag_scripts/primavera/eady_growth_rate/eady_growth_rate.py b/esmvaltool/diag_scripts/primavera/eady_growth_rate/eady_growth_rate.py index 02ee60d817..23946905f7 100644 --- a/esmvaltool/diag_scripts/primavera/eady_growth_rate/eady_growth_rate.py +++ b/esmvaltool/diag_scripts/primavera/eady_growth_rate/eady_growth_rate.py @@ -146,31 +146,20 @@ def save(self, egr, alias, data): egr.units = 'day-1' script = self.cfg[n.SCRIPT] - project = data[alias][0]['project'] - dataset = data[alias][0]['dataset'] - exp = data[alias][0]['exp'] - start = data[alias][0]['start_year'] - end = data[alias][0]['end_year'] - output_name = '{project}_' \ - '{dataset}_' \ - '{exp}_' \ - '{script}_' \ - '{start}_' \ - '{end}.nc'.format(project=project, - dataset=dataset, - exp=exp, - script=script, - start=start, - end=end) + info = data[alias][0] + keys = [str(info[key]) for key in ( + 'project', 'dataset', 'exp', 'ensemble', 'start_year', 'end_year' + ) if key in info] + output_name = '_'.join(keys)+'.nc' output_file = os.path.join(self.cfg[n.WORK_DIR], output_name) iris.save(egr, output_file) caption = ("{script} between {start} and {end}" "according to {dataset}").format( script=script.split('_'), - start=start, - end=end, - dataset=dataset + start=info['start_year'], + end=info['end_year'], + dataset=info['dataset'] ) ancestors = [] for i in range(len(data[alias])): From f25464222292d4a0c1217233be7bd484b385a084 Mon Sep 17 00:00:00 2001 From: sloosvel Date: Tue, 15 Sep 2020 17:50:17 +0200 Subject: [PATCH 27/47] First version of plot, pending to clean --- .../eady_growth_rate/eady_growth_rate.py | 50 ++++++++++++++++--- .../recipes/recipe_eady_growth_rate.yml | 1 + 2 files changed, 43 insertions(+), 8 deletions(-) diff --git a/esmvaltool/diag_scripts/primavera/eady_growth_rate/eady_growth_rate.py b/esmvaltool/diag_scripts/primavera/eady_growth_rate/eady_growth_rate.py index 23946905f7..fc46110372 100644 --- a/esmvaltool/diag_scripts/primavera/eady_growth_rate/eady_growth_rate.py +++ b/esmvaltool/diag_scripts/primavera/eady_growth_rate/eady_growth_rate.py @@ -1,17 +1,25 @@ import os import logging +import matplotlib +matplotlib.use('agg') + + import iris import iris.cube import iris.analysis import iris.util +import iris.quickplot as qplt +import matplotlib.pyplot as plt + +import cartopy.crs as ccrs import numpy as np from dask import array as da from esmvalcore.preprocessor import ( - regrid, annual_statistics, seasonal_statistics) + regrid, annual_statistics, seasonal_statistics, extract_levels) import esmvaltool.diag_scripts.shared import esmvaltool.diag_scripts.shared.names as n @@ -35,6 +43,7 @@ def __init__(self, config): self.compute_annual_mean = self.cfg['compute_annual_mean'] self.compute_seasonal_mean = self.cfg['compute_seasonal_mean'] + def compute(self): data = group_metadata(self.cfg['input_data'].values(), 'alias') for alias in data: @@ -61,11 +70,18 @@ def compute(self): cube_egr = ua.copy(egr * 86400) + cube_egr.standard_name = None + cube_egr.long_name = 'eady_growth_rate' + cube_egr.var_name = 'egr' + cube_egr.units = 'day-1' + + + # pending to clean this if self.compute_annual_mean: cube_egr = annual_statistics(cube_egr) elif self.compute_seasonal_mean: cube_egr = seasonal_statistics(cube_egr) - + self.seasonal_plots(cube_egr) if self.compute_climatology: cube_egr = cube_egr.collapsed('time', iris.analysis.MEAN) @@ -139,17 +155,35 @@ def eady_growth_rate(self, fcor, ua, zg, brunt): return egr - def save(self, egr, alias, data): - egr.standard_name = None - egr.long_name = 'eady_growth_rate' - egr.var_name = 'egr' - egr.units = 'day-1' + def seasonal_plots(self, egr): + try: + levels = self.cfg['plot_levels'] + except KeyError: + levels = egr.coord('air_pressure').points + for level in levels: + cube = extract_levels(egr, level, scheme='linear') + crs_latlon = ccrs.PlateCarree() + ax = plt.axes(projection=ccrs.PlateCarree()) + ax.coastlines(linewidth=1, color='black') + # North Atlantic + ax.set_extent((-90.0, 30.0, 20.0, 80.0), crs=crs_latlon) + ax.set_yticks(np.linspace(25,75,6)) + qplt.contourf(cube, levels=np.arange(0, np.max(cube.data), 0.05)) + extension = self.cfg['output_file_type'] + diagnostic = self.cfg['script'] + plotname = '_'.join([diagnostic, str(level), f'.{extension}']) # fix this + plt.savefig(os.path.join(self.cfg[n.PLOT_DIR], plotname)) + plt.close() + + + + def save(self, egr, alias, data): script = self.cfg[n.SCRIPT] info = data[alias][0] keys = [str(info[key]) for key in ( 'project', 'dataset', 'exp', 'ensemble', 'start_year', 'end_year' - ) if key in info] + ) if key in info] # fix this output_name = '_'.join(keys)+'.nc' output_file = os.path.join(self.cfg[n.WORK_DIR], output_name) iris.save(egr, output_file) diff --git a/esmvaltool/recipes/recipe_eady_growth_rate.yml b/esmvaltool/recipes/recipe_eady_growth_rate.yml index 5bea4a4544..12bfe30c3b 100644 --- a/esmvaltool/recipes/recipe_eady_growth_rate.yml +++ b/esmvaltool/recipes/recipe_eady_growth_rate.yml @@ -96,3 +96,4 @@ diagnostics: compute_annual_mean: false compute_seasonal_mean: false compute_climatology: true + plot_levels: [70000] From aeaf4c683196daded9e59ae674b04022d2832cdb Mon Sep 17 00:00:00 2001 From: sloosvel Date: Tue, 22 Sep 2020 15:52:35 +0200 Subject: [PATCH 28/47] Clean recipe and diag --- .../eady_growth_rate/eady_growth_rate.py | 100 ++++++++---------- .../recipes/recipe_eady_growth_rate.yml | 81 ++++++-------- 2 files changed, 81 insertions(+), 100 deletions(-) diff --git a/esmvaltool/diag_scripts/primavera/eady_growth_rate/eady_growth_rate.py b/esmvaltool/diag_scripts/primavera/eady_growth_rate/eady_growth_rate.py index fc46110372..ebd585753f 100644 --- a/esmvaltool/diag_scripts/primavera/eady_growth_rate/eady_growth_rate.py +++ b/esmvaltool/diag_scripts/primavera/eady_growth_rate/eady_growth_rate.py @@ -1,31 +1,25 @@ -import os import logging +import os +import sys -import matplotlib -matplotlib.use('agg') - - +import cartopy.crs as ccrs import iris -import iris.cube import iris.analysis -import iris.util +import iris.cube import iris.quickplot as qplt +import iris.util +import matplotlib import matplotlib.pyplot as plt - -import cartopy.crs as ccrs - import numpy as np - from dask import array as da -from esmvalcore.preprocessor import ( - regrid, annual_statistics, seasonal_statistics, extract_levels) - import esmvaltool.diag_scripts.shared import esmvaltool.diag_scripts.shared.names as n -from esmvaltool.diag_scripts.shared import group_metadata -from esmvaltool.diag_scripts.shared import ProvenanceLogger +from esmvalcore.preprocessor import (annual_statistics, extract_levels, regrid, + seasonal_statistics) +from esmvaltool.diag_scripts.shared import ProvenanceLogger, group_metadata +matplotlib.use('agg') logger = logging.getLogger(os.path.basename(__file__)) @@ -39,10 +33,7 @@ def __init__(self, config): self.g = 9.80665 self.con = 0.3098 self.omega = 7.292e-5 - self.compute_climatology = self.cfg['compute_climatology'] - self.compute_annual_mean = self.cfg['compute_annual_mean'] - self.compute_seasonal_mean = self.cfg['compute_seasonal_mean'] - + self.time_statistic = self.cfg['time_statistic'] def compute(self): data = group_metadata(self.cfg['input_data'].values(), 'alias') @@ -75,15 +66,18 @@ def compute(self): cube_egr.var_name = 'egr' cube_egr.units = 'day-1' - - # pending to clean this - if self.compute_annual_mean: + if self.time_statistic == 'annual_mean': cube_egr = annual_statistics(cube_egr) - elif self.compute_seasonal_mean: + cube_egr = cube_egr.collapsed('time', iris.analysis.MEAN) + elif self.time_statistic == 'seasonal_mean': cube_egr = seasonal_statistics(cube_egr) - self.seasonal_plots(cube_egr) - if self.compute_climatology: cube_egr = cube_egr.collapsed('time', iris.analysis.MEAN) + self.seasonal_plots(cube_egr, alias) + else: + logger.info( + "Parameter time_statistic is not well set in the recipe." + "Must be 'annual' or 'seasonal'") + sys.exit() self.save(cube_egr, alias, data) @@ -92,12 +86,9 @@ def potential_temperature(self, ta, plev): long_name='reference_pressure', units='hPa') p0.convert_units(plev.units) - p = (p0.points/plev.points)**(2/7) + p = (p0.points / plev.points)**(2 / 7) theta = ta * iris.util.broadcast_to_shape( - p, - ta.shape, - ta.coord_dims('air_pressure') - ) + p, ta.shape, ta.coord_dims('air_pressure')) theta.long_name = 'potential_air_temperature' return theta @@ -112,14 +103,14 @@ def vertical_integration(self, x, y): (y[:, 1, :, :].lazy_data() - y[:, 0, :, :].lazy_data())) dxdy_centre = ((x[:, 2:plevs, :, :].lazy_data() - - x[:, 0:plevs-2, :, :].lazy_data()) / + x[:, 0:plevs - 2, :, :].lazy_data()) / (y[:, 2:plevs, :, :].lazy_data() - - y[:, 0:plevs-2, :, :].lazy_data())) + y[:, 0:plevs - 2, :, :].lazy_data())) - dxdy_end = ((x[:, plevs-1, :, :].lazy_data() - - x[:, plevs-2, :, :].lazy_data()) / - (y[:, plevs-1, :, :].lazy_data() - - y[:, plevs-2, :, :].lazy_data())) + dxdy_end = ((x[:, plevs - 1, :, :].lazy_data() - + x[:, plevs - 2, :, :].lazy_data()) / + (y[:, plevs - 1, :, :].lazy_data() - + y[:, plevs - 2, :, :].lazy_data())) bounds = [dxdy_end, dxdy_0] stacked_bounds = da.stack(bounds, axis=1) @@ -155,10 +146,12 @@ def eady_growth_rate(self, fcor, ua, zg, brunt): return egr - def seasonal_plots(self, egr): + def seasonal_plots(self, egr, alias): try: levels = self.cfg['plot_levels'] except KeyError: + logger.info("Parameter plot_levels is not set in the recipe." + "Plotting all pressure levels instead.") levels = egr.coord('air_pressure').points for level in levels: cube = extract_levels(egr, level, scheme='linear') @@ -167,34 +160,33 @@ def seasonal_plots(self, egr): ax.coastlines(linewidth=1, color='black') # North Atlantic ax.set_extent((-90.0, 30.0, 20.0, 80.0), crs=crs_latlon) - ax.set_yticks(np.linspace(25,75,6)) + ax.set_yticks(np.linspace(25, 75, 6)) qplt.contourf(cube, levels=np.arange(0, np.max(cube.data), 0.05)) extension = self.cfg['output_file_type'] diagnostic = self.cfg['script'] - plotname = '_'.join([diagnostic, str(level), f'.{extension}']) # fix this + plotname = '_'.join([alias, diagnostic, + str(int(level))]) + f'.{extension}' plt.savefig(os.path.join(self.cfg[n.PLOT_DIR], plotname)) plt.close() - - - def save(self, egr, alias, data): script = self.cfg[n.SCRIPT] info = data[alias][0] - keys = [str(info[key]) for key in ( - 'project', 'dataset', 'exp', 'ensemble', 'start_year', 'end_year' - ) if key in info] # fix this - output_name = '_'.join(keys)+'.nc' + keys = [ + str(info[key]) for key in ('project', 'dataset', 'exp', 'ensemble', + 'diagnostic', 'start_year', 'end_year') + if key in info + ] + output_name = '_'.join(keys) + '.nc' output_file = os.path.join(self.cfg[n.WORK_DIR], output_name) iris.save(egr, output_file) caption = ("{script} between {start} and {end}" - "according to {dataset}").format( - script=script.split('_'), - start=info['start_year'], - end=info['end_year'], - dataset=info['dataset'] - ) + "according to {dataset}").format(script=script.replace( + " ", '_'), + start=info['start_year'], + end=info['end_year'], + dataset=info['dataset']) ancestors = [] for i in range(len(data[alias])): ancestors.append(data[alias][i]['filename']) @@ -204,7 +196,7 @@ def save(self, egr, alias, data): 'autors': ['sanchez-gomez_emilia'], 'references': ['acknow_project'], 'ancestors': ancestors - } + } with ProvenanceLogger(self.cfg) as provenance_logger: provenance_logger.log(output_file, record) diff --git a/esmvaltool/recipes/recipe_eady_growth_rate.yml b/esmvaltool/recipes/recipe_eady_growth_rate.yml index 12bfe30c3b..dad979e032 100644 --- a/esmvaltool/recipes/recipe_eady_growth_rate.yml +++ b/esmvaltool/recipes/recipe_eady_growth_rate.yml @@ -2,12 +2,14 @@ --- documentation: description: | - Recipe to compute the annual, summer and winter Eady Growth Rates. The - output produces netcdf files for each model and season. + Recipe to compute the annual mean or the seasonal mean the Eady Growth Rate (EGR). + The output produces netcdf files for each model. In the case of the seasonal means, a plot is + produced for each specified level showing the EGR values over the North-Atlantic region. authors: - sanchez-gomez_emilia + # - moreno-chamarro_eduardo add to authors maintainer: - loosveldt-tomas_saskia @@ -16,84 +18,71 @@ documentation: - primavera datasets: - - {dataset: CMCC-CM2-HR4, project: CMIP6, exp: highresSST-present, - ensemble: r1i1p1f1, mip: Amon, grid: gn, start_year: 1950, end_year: 2014} - - {dataset: CNRM-CM6-1, project: CMIP6, exp: highresSST-present, - ensemble: r1i1p1f2, mip: Amon, grid: gr, start_year: 1950, end_year: 2014} - - {dataset: CNRM-CM6-1-HR, project: CMIP6, exp: highresSST-present, - ensemble: r1i1p1f2, mip: Amon, grid: gr, start_year: 1950, end_year: 2014} - - {dataset: ECMWF-IFS-HR, project: CMIP6, exp: highresSST-present, - ensemble: r1i1p1f1, mip: Amon, grid: gr, start_year: 1950, end_year: 2014} - - {dataset: ECMWF-IFS-LR, project: CMIP6, exp: highresSST-present, - ensemble: r1i1p1f1, mip: Amon, grid: gr, start_year: 1950, end_year: 2014} - - {dataset: HadGEM3-GC31-HM, project: CMIP6, exp: highresSST-present, - ensemble: r1i1p1f1, mip: Amon, grid: gn, start_year: 1950, end_year: 2014} - - {dataset: HadGEM3-GC31-LM, project: CMIP6, exp: highresSST-present, - ensemble: r1i1p1f1, mip: Amon, grid: gn, start_year: 1950, end_year: 2014} - - {dataset: HadGEM3-GC31-MM, project: CMIP6, exp: highresSST-present, - ensemble: r1i1p1f1, mip: Amon, grid: gn, start_year: 1950, end_year: 2014} - - {dataset: MPI-ESM1-2-XR, project: CMIP6, exp: highresSST-present, - ensemble: r1i1p1f1, mip: Amon, grid: gn, start_year: 1950, end_year: 2014} - - {dataset: MPI-ESM1-2-HR, project: CMIP6, exp: highresSST-present, - ensemble: r1i1p1f1, mip: Amon, grid: gn, start_year: 1950, end_year: 2014} + - {dataset: CMCC-CM2-HR4, project: CMIP6, exp: highresSST-present, + ensemble: r1i1p1f1, mip: Amon, grid: gn, start_year: 1950, end_year: 2014} + - {dataset: CNRM-CM6-1, project: CMIP6, exp: highresSST-present, + ensemble: r1i1p1f2, mip: Amon, grid: gr, start_year: 1950, end_year: 2014} + - {dataset: CNRM-CM6-1-HR, project: CMIP6, exp: highresSST-present, + ensemble: r1i1p1f2, mip: Amon, grid: gr, start_year: 1950, end_year: 2014} + - {dataset: ECMWF-IFS-HR, project: CMIP6, exp: highresSST-present, + ensemble: r1i1p1f1, mip: Amon, grid: gr, start_year: 1950, end_year: 2014} + - {dataset: ECMWF-IFS-LR, project: CMIP6, exp: highresSST-present, + ensemble: r1i1p1f1, mip: Amon, grid: gr, start_year: 1950, end_year: 2014} + - {dataset: HadGEM3-GC31-HM, project: CMIP6, exp: highresSST-present, + ensemble: r1i1p1f1, mip: Amon, grid: gn, start_year: 1950, end_year: 2014} + - {dataset: HadGEM3-GC31-LM, project: CMIP6, exp: highresSST-present, + ensemble: r1i1p1f1, mip: Amon, grid: gn, start_year: 1950, end_year: 2014} + - {dataset: HadGEM3-GC31-MM, project: CMIP6, exp: highresSST-present, + ensemble: r1i1p1f1, mip: Amon, grid: gn, start_year: 1950, end_year: 2014} + - {dataset: MPI-ESM1-2-XR, project: CMIP6, exp: highresSST-present, + ensemble: r1i1p1f1, mip: Amon, grid: gn, start_year: 1950, end_year: 2014} + - {dataset: MPI-ESM1-2-HR, project: CMIP6, exp: highresSST-present, + ensemble: r1i1p1f1, mip: Amon, grid: gn, start_year: 1950, end_year: 2014} preprocessors: - annual_mean: - annual_statistics: - summer_mean: + summer: extract_season: season: 'JJA' - seasonal_statistics: - winter_mean: + winter: extract_season: season: 'DJF' - seasonal_statistics: diagnostics: annual_egr: variables: ta: - preprocessor: annual_mean zg: - preprocessor: annual_mean ua: - preprocessor: annual_mean scripts: annual_eady_growth_rate: script: primavera/eady_growth_rate/eady_growth_rate.py - compute_annual_mean: false - compute_seasonal_mean: false - compute_climatology: true + time_statistic: 'annual_mean' summer_egr: variables: ta: - preprocessor: summer_mean + preprocessor: summer zg: - preprocessor: summer_mean + preprocessor: summer ua: - preprocessor: summer_mean + preprocessor: summer scripts: summer_eady_growth_rate: script: primavera/eady_growth_rate/eady_growth_rate.py - compute_annual_mean: false - compute_seasonal_mean: false - compute_climatology: true + time_statistic: 'seasonal_mean' winter_egr: variables: ta: - preprocessor: winter_mean + preprocessor: winter zg: - preprocessor: winter_mean + preprocessor: winter ua: - preprocessor: winter_mean + preprocessor: winter scripts: winter_eady_growth_rate: script: primavera/eady_growth_rate/eady_growth_rate.py - compute_annual_mean: false - compute_seasonal_mean: false - compute_climatology: true + time_statistic: 'seasonal_mean' plot_levels: [70000] From babbcc52dea8fadd30b9037e8eb21ef8709d5401 Mon Sep 17 00:00:00 2001 From: sloosvel Date: Tue, 22 Sep 2020 15:52:54 +0200 Subject: [PATCH 29/47] Add documentation, pending to add figure --- .../recipes/recipe_eady_growth_rate.rst | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 doc/sphinx/source/recipes/recipe_eady_growth_rate.rst diff --git a/doc/sphinx/source/recipes/recipe_eady_growth_rate.rst b/doc/sphinx/source/recipes/recipe_eady_growth_rate.rst new file mode 100644 index 0000000000..be823b7d21 --- /dev/null +++ b/doc/sphinx/source/recipes/recipe_eady_growth_rate.rst @@ -0,0 +1,61 @@ +.. _recipes_eady_growth_rate: + +Eady growth rate +===== + +Overview +-------- + +This recipe computes the Eady growth rate and performs the annual and seasonal means, storing +the results for each dataset. +For the seasonal means, the results are plotted over the North-Atlantic region for the selected +levels to reproduce Figure ? from PAPER. + + +Available recipes and diagnostics +--------------------------------- + +Recipes are stored in esmvaltool/recipes/ + + * recipe_eady_growth_rate.yml + +Diagnostics are stored in esmvaltool/diag_scripts/eady_growth_rate/ + + * eady_growth_rate.py: Computes and stores the eady growth rate. + Plots can be produced for the seasonal mean over the North Atlantic region. + + +User settings in recipe +----------------------- + +#. Script eady_growth_rate.py + + *Required settings for script* + + * time_statistic: Set to 'annual' to compute the annual mean. Set to 'seasonal' to compute the seasonal mean. + + *Optional settings for script* + + * plot_levels: list of pressure levels to be plotted for the seasonal mean. If not specified, all levels will be plotted. + + +Variables +--------- + +* ta (atmos, monthly mean, longitude latitude level time) +* zg (atmos, monthly mean, longitude latitude level time) +* ua (atmos, monthly mean, longitude latitude level time) + +References +---------- + +* xxx + +Example plots # TO BE ADDED +------------- + +.. _fig_eady_growth_rate: +.. figure:: /recipes/figures//awesome1.png + :align: center + + Add figure caption here. From fad670bee0283ec51fa3a54359b402dce6c30861 Mon Sep 17 00:00:00 2001 From: sloosvel Date: Wed, 23 Sep 2020 18:42:59 +0200 Subject: [PATCH 30/47] Add figure to documentation --- ...M3-GC31-LM_winter_eady_growth_rate_70000.png | Bin 0 -> 89993 bytes .../source/recipes/recipe_eady_growth_rate.rst | 4 ++-- 2 files changed, 2 insertions(+), 2 deletions(-) create mode 100644 doc/sphinx/source/recipes/figures/eady_growth_rate/HadGEM3-GC31-LM_winter_eady_growth_rate_70000.png diff --git a/doc/sphinx/source/recipes/figures/eady_growth_rate/HadGEM3-GC31-LM_winter_eady_growth_rate_70000.png b/doc/sphinx/source/recipes/figures/eady_growth_rate/HadGEM3-GC31-LM_winter_eady_growth_rate_70000.png new file mode 100644 index 0000000000000000000000000000000000000000..6c2eb7bf9b3e5d254a279f343ae635d8f1e1b7ba GIT binary patch literal 89993 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�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/awesome1.png +.. figure:: /recipes/figures/eady_growth_rate/HadGEM3-GC31-LM_winter_eady_growth_rate_70000.png :align: center Add figure caption here. From 44fde952a93f8362544024d5624d29d07d0f279b Mon Sep 17 00:00:00 2001 From: sloosvel Date: Tue, 24 Nov 2020 12:00:39 +0100 Subject: [PATCH 31/47] Add missing author --- .zenodo.json | 4 ++++ doc/sphinx/source/recipes/recipe_eady_growth_rate.rst | 8 ++------ esmvaltool/config-references.yml | 4 ++++ .../primavera/eady_growth_rate/eady_growth_rate.py | 4 ++-- esmvaltool/recipes/recipe_eady_growth_rate.yml | 2 +- 5 files changed, 13 insertions(+), 9 deletions(-) diff --git a/.zenodo.json b/.zenodo.json index b89a54d409..26395f4aab 100644 --- a/.zenodo.json +++ b/.zenodo.json @@ -238,6 +238,10 @@ "name": "Mohr, Christian Wilhelm", "orcid": "0000-0003-2656-1802" }, + { + "affiliation": "BSC, Spain", + "name": "Moreno-Chamarro, Eduardo" + }, { "affiliation": "University of Arizona, USA", "name": "Amarjiit, Pandde" diff --git a/doc/sphinx/source/recipes/recipe_eady_growth_rate.rst b/doc/sphinx/source/recipes/recipe_eady_growth_rate.rst index 95b2506566..ba5d00c6fc 100644 --- a/doc/sphinx/source/recipes/recipe_eady_growth_rate.rst +++ b/doc/sphinx/source/recipes/recipe_eady_growth_rate.rst @@ -9,7 +9,7 @@ Overview This recipe computes the Eady growth rate and performs the annual and seasonal means, storing the results for each dataset. For the seasonal means, the results are plotted over the North-Atlantic region for the selected -levels to reproduce Figure ? from PAPER. +pressure levels. Available recipes and diagnostics @@ -46,10 +46,6 @@ Variables * zg (atmos, monthly mean, longitude latitude level time) * ua (atmos, monthly mean, longitude latitude level time) -References ----------- - -* xxx Example plots ------------- @@ -58,4 +54,4 @@ Example plots .. figure:: /recipes/figures/eady_growth_rate/HadGEM3-GC31-LM_winter_eady_growth_rate_70000.png :align: center - Add figure caption here. + Eady Growth Rate values over the North-Atlantic region at diff --git a/esmvaltool/config-references.yml b/esmvaltool/config-references.yml index d0c92b8ffd..c3cbbed855 100644 --- a/esmvaltool/config-references.yml +++ b/esmvaltool/config-references.yml @@ -321,6 +321,10 @@ authors: name: Mello, Felipe institute: INPE, Brazil orcid: https://orcid.org/0000-0002-8832-2869 + moreno-chamarro_eduardo: + name: Moreno-Chamarro, Eduardo + institute: BSC, Spain + orcid: dalvi_mohit: name: Dalvi, Mohit institute: MetOffice, UK diff --git a/esmvaltool/diag_scripts/primavera/eady_growth_rate/eady_growth_rate.py b/esmvaltool/diag_scripts/primavera/eady_growth_rate/eady_growth_rate.py index ebd585753f..2acba8cca6 100644 --- a/esmvaltool/diag_scripts/primavera/eady_growth_rate/eady_growth_rate.py +++ b/esmvaltool/diag_scripts/primavera/eady_growth_rate/eady_growth_rate.py @@ -76,7 +76,7 @@ def compute(self): else: logger.info( "Parameter time_statistic is not well set in the recipe." - "Must be 'annual' or 'seasonal'") + "Must be 'annual_mean' or 'seasonal_mean'") sys.exit() self.save(cube_egr, alias, data) @@ -193,7 +193,7 @@ def save(self, egr, alias, data): record = { 'caption': caption, 'domains': ['global'], - 'autors': ['sanchez-gomez_emilia'], + 'authors': ['sanchez-gomez_emilia', 'moreno-chamarro_eduardo'], 'references': ['acknow_project'], 'ancestors': ancestors } diff --git a/esmvaltool/recipes/recipe_eady_growth_rate.yml b/esmvaltool/recipes/recipe_eady_growth_rate.yml index dad979e032..832e6f2d26 100644 --- a/esmvaltool/recipes/recipe_eady_growth_rate.yml +++ b/esmvaltool/recipes/recipe_eady_growth_rate.yml @@ -9,7 +9,7 @@ documentation: authors: - sanchez-gomez_emilia - # - moreno-chamarro_eduardo add to authors + - moreno-chamarro_eduardo maintainer: - loosveldt-tomas_saskia From 47d9154ae2c091015fed2a6886241623e2b6cc84 Mon Sep 17 00:00:00 2001 From: sloosvel Date: Tue, 24 Nov 2020 12:07:34 +0100 Subject: [PATCH 32/47] Fix documentation --- doc/sphinx/source/recipes/recipe_eady_growth_rate.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/sphinx/source/recipes/recipe_eady_growth_rate.rst b/doc/sphinx/source/recipes/recipe_eady_growth_rate.rst index ba5d00c6fc..9a35f202b9 100644 --- a/doc/sphinx/source/recipes/recipe_eady_growth_rate.rst +++ b/doc/sphinx/source/recipes/recipe_eady_growth_rate.rst @@ -1,7 +1,7 @@ .. _recipes_eady_growth_rate: Eady growth rate -===== +========================================================================================================================== Overview -------- @@ -54,4 +54,4 @@ Example plots .. figure:: /recipes/figures/eady_growth_rate/HadGEM3-GC31-LM_winter_eady_growth_rate_70000.png :align: center - Eady Growth Rate values over the North-Atlantic region at + Eady Growth Rate values over the North-Atlantic region at 70000 Pa. From bc35afbb5bdd30531be76f549b62f695fc5e8672 Mon Sep 17 00:00:00 2001 From: sloosvel Date: Tue, 24 Nov 2020 12:26:29 +0100 Subject: [PATCH 33/47] Fix more documentation --- doc/sphinx/source/recipes/index.rst | 1 + doc/sphinx/source/recipes/recipe_eady_growth_rate.rst | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/sphinx/source/recipes/index.rst b/doc/sphinx/source/recipes/index.rst index 0272e86e6c..598a28675e 100644 --- a/doc/sphinx/source/recipes/index.rst +++ b/doc/sphinx/source/recipes/index.rst @@ -19,6 +19,7 @@ Atmosphere recipe_consecdrydays recipe_deangelis15nat recipe_diurnal_temperature_index + recipe_eady_growth_rate recipe_extreme_events recipe_eyring06jgr recipe_heatwaves_coldwaves diff --git a/doc/sphinx/source/recipes/recipe_eady_growth_rate.rst b/doc/sphinx/source/recipes/recipe_eady_growth_rate.rst index 9a35f202b9..7208a5a2ed 100644 --- a/doc/sphinx/source/recipes/recipe_eady_growth_rate.rst +++ b/doc/sphinx/source/recipes/recipe_eady_growth_rate.rst @@ -22,7 +22,7 @@ Recipes are stored in esmvaltool/recipes/ Diagnostics are stored in esmvaltool/diag_scripts/eady_growth_rate/ * eady_growth_rate.py: Computes and stores the eady growth rate. - Plots can be produced for the seasonal mean over the North Atlantic region. + Plots can be produced for the seasonal mean over the North Atlantic region. User settings in recipe From ab9a78fca56e860e8916f6a745e8e6aa83c6ded8 Mon Sep 17 00:00:00 2001 From: sloosvel Date: Tue, 24 Nov 2020 17:05:58 +0100 Subject: [PATCH 34/47] Address suggestions from code review --- .../eady_growth_rate/eady_growth_rate.py | 220 +++++++++++++----- 1 file changed, 165 insertions(+), 55 deletions(-) diff --git a/esmvaltool/diag_scripts/primavera/eady_growth_rate/eady_growth_rate.py b/esmvaltool/diag_scripts/primavera/eady_growth_rate/eady_growth_rate.py index 2acba8cca6..878ce170a9 100644 --- a/esmvaltool/diag_scripts/primavera/eady_growth_rate/eady_growth_rate.py +++ b/esmvaltool/diag_scripts/primavera/eady_growth_rate/eady_growth_rate.py @@ -8,58 +8,78 @@ import iris.cube import iris.quickplot as qplt import iris.util -import matplotlib import matplotlib.pyplot as plt import numpy as np from dask import array as da import esmvaltool.diag_scripts.shared -import esmvaltool.diag_scripts.shared.names as n +import esmvaltool.diag_scripts.shared.names as shared_names from esmvalcore.preprocessor import (annual_statistics, extract_levels, regrid, seasonal_statistics) from esmvaltool.diag_scripts.shared import ProvenanceLogger, group_metadata -matplotlib.use('agg') logger = logging.getLogger(os.path.basename(__file__)) class EadyGrowthRate(object): + """Class used to compute the Eady Growth Rate.""" def __init__(self, config): + """ + Set diagnostic parameters and constants. + + Parameters + ---------- + config : dict + Dictionary containing configuration settings + """ + self.cfg = config self.filenames = esmvaltool.diag_scripts.shared.Datasets(self.cfg) self.fill_value = 1e20 + """Fill Value.""" self.ref_p = 1000.0 - self.g = 9.80665 + """Reference Pressure [Pa]""" + self.gravity = 9.80665 + """Gravity [m/s]""" self.con = 0.3098 + """Constant""" self.omega = 7.292e-5 + """Rotation of the Earth [rad/s]""" self.time_statistic = self.cfg['time_statistic'] + """Time statistic to perform""" def compute(self): + """ + Computes the Eady Growth Rate and + either it's annual or the seasonal mean. + """ data = group_metadata(self.cfg['input_data'].values(), 'alias') for alias in data: var = group_metadata(data[alias], 'short_name') - ta = iris.load_cube(var['ta'][0]['filename']) - plev = ta.coord('air_pressure') + temperature = iris.load_cube(var['ta'][0]['filename']) + plev = temperature.coord('air_pressure') - theta = self.potential_temperature(ta, plev) + theta = self.potential_temperature(temperature, plev) - del ta + del temperature - zg = iris.load_cube(var['zg'][0]['filename']) + geopotential = iris.load_cube(var['zg'][0]['filename']) - brunt = self.brunt_vaisala_frq(theta, zg) + brunt = self.brunt_vaisala_frq(theta, geopotential) - lats = zg.coord('latitude') - fcor = self.coriolis(lats, zg.shape) + lats = geopotential.coord('latitude') + fcor = self.coriolis(lats, geopotential.shape) - ua = iris.load_cube(var['ua'][0]['filename']) - if ua.shape is not zg.shape: - ua = regrid(ua, zg, scheme='linear') + eastward_wind = iris.load_cube(var['ua'][0]['filename']) + if eastward_wind.shape is not geopotential.shape: + eastward_wind = regrid( + eastward_wind, geopotential, scheme='linear') - egr = self.eady_growth_rate(fcor, ua, zg, brunt) + egr = self.eady_growth_rate( + fcor, eastward_wind, geopotential, brunt) - cube_egr = ua.copy(egr * 86400) + cube_egr = eastward_wind.copy(egr * 86400) cube_egr.standard_name = None cube_egr.long_name = 'eady_growth_rate' @@ -81,36 +101,70 @@ def compute(self): self.save(cube_egr, alias, data) - def potential_temperature(self, ta, plev): - p0 = iris.coords.AuxCoord(self.ref_p, - long_name='reference_pressure', - units='hPa') - p0.convert_units(plev.units) - p = (p0.points / plev.points)**(2 / 7) - theta = ta * iris.util.broadcast_to_shape( - p, ta.shape, ta.coord_dims('air_pressure')) + def potential_temperature(self, temperature, plev): + """Computes the potential temperature. + + Parameters + ---------- + temperature: iris.cube.Cube + Cube of air temperature ta. + plev: iris.coords.Coord + Pressure level coordinates + + Returns + ------- + theta: iris.cube.Cube + Cube of potential temperature theta. + + """ + reference_pressure = iris.coords.AuxCoord( + self.ref_p, + long_name='reference_pressure', + units='hPa') + reference_pressure.convert_units(plev.units) + pressure = (reference_pressure.points / plev.points)**(2 / 7) + theta = temperature * iris.util.broadcast_to_shape( + pressure, + temperature.shape, + temperature.coord_dims('air_pressure')) theta.long_name = 'potential_air_temperature' return theta - def vertical_integration(self, x, y): - # Perform a non-cyclic centered finite-difference to integrate - # along pressure levels - - plevs = x.shape[1] - - dxdy_0 = ((x[:, 1, :, :].lazy_data() - x[:, 0, :, :].lazy_data()) / - (y[:, 1, :, :].lazy_data() - y[:, 0, :, :].lazy_data())) - - dxdy_centre = ((x[:, 2:plevs, :, :].lazy_data() - - x[:, 0:plevs - 2, :, :].lazy_data()) / - (y[:, 2:plevs, :, :].lazy_data() - - y[:, 0:plevs - 2, :, :].lazy_data())) - - dxdy_end = ((x[:, plevs - 1, :, :].lazy_data() - - x[:, plevs - 2, :, :].lazy_data()) / - (y[:, plevs - 1, :, :].lazy_data() - - y[:, plevs - 2, :, :].lazy_data())) + def vertical_integration(self, var_x, var_y): + """Perform a non-cyclic centered finite-difference to integrate + variable x with respect to variable y along pressure levels. + + Parameters + ---------- + x: iris.cube.Cube + Cube of variable x. + y: iris.cube.Cube + Cube of variable y. + + Returns: + -------- + dxdy: iris.cube.Cube + Cube of variable integrated along pressure levels. + """ + + plevs = var_x.shape[1] + + dxdy_0 = ( + (var_x[:, 1, :, :].lazy_data() - var_x[:, 0, :, :].lazy_data()) / + (var_y[:, 1, :, :].lazy_data() - var_y[:, 0, :, :].lazy_data())) + + dxdy_centre = ( + (var_x[:, 2:plevs, :, :].lazy_data() - + var_x[:, 0:plevs - 2, :, :].lazy_data()) / + (var_y[:, 2:plevs, :, :].lazy_data() - + var_y[:, 0:plevs - 2, :, :].lazy_data())) + + dxdy_end = ( + (var_x[:, plevs - 1, :, :].lazy_data() - + var_x[:, plevs - 2, :, :].lazy_data()) / + (var_y[:, plevs - 1, :, :].lazy_data() - + var_y[:, plevs - 2, :, :].lazy_data())) bounds = [dxdy_end, dxdy_0] stacked_bounds = da.stack(bounds, axis=1) @@ -124,29 +178,81 @@ def vertical_integration(self, x, y): return dxdy - def brunt_vaisala_frq(self, theta, zg): - dthdz = self.vertical_integration(theta, zg) + def brunt_vaisala_frq(self, theta, geopotential): + """Compute Brunt-Väisälä frequency. + + Parameters + ---------- + theta: iris.cube.Cube + Cube of potential temperature. + geopotential: iris.cube.Cube + Cube of variable zg. + + Returns: + -------- + brunt: da.array + Array containing Brunt-Väisälä frequency. + """ + dthdz = self.vertical_integration(theta, geopotential) dthdz = da.where(dthdz > 0, dthdz, 0) - buoy = (self.g / theta.lazy_data()) * dthdz + buoy = (self.gravity / theta.lazy_data()) * dthdz brunt = da.sqrt(buoy) brunt = da.where(brunt != 0, brunt, self.fill_value) return brunt def coriolis(self, lats, ndim): + """Compute Coriolis force. + + Parameters + ---------- + lats: iris.coord.Coord + Latitude coordinate. + ndim: int + Number of dimension. + + Returns: + -------- + fcor: da.array + Array containing Coriolis force. + """ fcor = 2.0 * self.omega * np.sin(np.radians(lats.points)) fcor = fcor[np.newaxis, np.newaxis, :, np.newaxis] fcor = da.broadcast_to(fcor, ndim) return fcor - def eady_growth_rate(self, fcor, ua, zg, brunt): - dudz = self.vertical_integration(ua, zg) + def eady_growth_rate(self, fcor, eastward_wind, geopotential, brunt): + """Compute Eady Growth Rate. + + Parameters + ---------- + eastward_wind: iris.cube.Cube + Cube containing variable ua. + geopotential: iris.cube.Cube + Cube containing variable zg. + brunt: da.array + Array containing Brunt-Väisäla frequency + + Returns: + -------- + egr: da.array + Array containing Eady Growth Rate. + """ + dudz = self.vertical_integration(eastward_wind, geopotential) egr = self.con * abs(fcor) * abs(dudz) / brunt return egr def seasonal_plots(self, egr, alias): + """Plot seasonal Eady Growth rate values. + Parameters + ---------- + egr: iris.cube.Cube + Cube containing variable egr. + alias: str + Alias of the dataset. + """ try: levels = self.cfg['plot_levels'] except KeyError: @@ -156,21 +262,23 @@ def seasonal_plots(self, egr, alias): for level in levels: cube = extract_levels(egr, level, scheme='linear') crs_latlon = ccrs.PlateCarree() - ax = plt.axes(projection=ccrs.PlateCarree()) - ax.coastlines(linewidth=1, color='black') + axes = plt.axes(projection=ccrs.PlateCarree()) + axes.coastlines(linewidth=1, color='black') # North Atlantic - ax.set_extent((-90.0, 30.0, 20.0, 80.0), crs=crs_latlon) - ax.set_yticks(np.linspace(25, 75, 6)) + axes.set_extent((-90.0, 30.0, 20.0, 80.0), crs=crs_latlon) + axes.set_yticks(np.linspace(25, 75, 6)) qplt.contourf(cube, levels=np.arange(0, np.max(cube.data), 0.05)) extension = self.cfg['output_file_type'] diagnostic = self.cfg['script'] plotname = '_'.join([alias, diagnostic, str(int(level))]) + f'.{extension}' - plt.savefig(os.path.join(self.cfg[n.PLOT_DIR], plotname)) + plt.savefig(os.path.join( + self.cfg[shared_names.PLOT_DIR], plotname)) plt.close() def save(self, egr, alias, data): - script = self.cfg[n.SCRIPT] + """Save results and write provenance.""" + script = self.cfg[shared_names.SCRIPT] info = data[alias][0] keys = [ str(info[key]) for key in ('project', 'dataset', 'exp', 'ensemble', @@ -178,7 +286,8 @@ def save(self, egr, alias, data): if key in info ] output_name = '_'.join(keys) + '.nc' - output_file = os.path.join(self.cfg[n.WORK_DIR], output_name) + output_file = os.path.join( + self.cfg[shared_names.WORK_DIR], output_name) iris.save(egr, output_file) caption = ("{script} between {start} and {end}" @@ -202,6 +311,7 @@ def save(self, egr, alias, data): def main(): + """Run Eady Growth Rate diagnostic""" with esmvaltool.diag_scripts.shared.run_diagnostic() as config: EadyGrowthRate(config).compute() From ba32f33cacd53e72993ef7c8d62000cde81d4c31 Mon Sep 17 00:00:00 2001 From: sloosvel Date: Wed, 25 Nov 2020 10:28:47 +0100 Subject: [PATCH 35/47] Fix style issues --- .../eady_growth_rate/eady_growth_rate.py | 77 +++++++++++-------- 1 file changed, 46 insertions(+), 31 deletions(-) diff --git a/esmvaltool/diag_scripts/primavera/eady_growth_rate/eady_growth_rate.py b/esmvaltool/diag_scripts/primavera/eady_growth_rate/eady_growth_rate.py index 878ce170a9..f912d83088 100644 --- a/esmvaltool/diag_scripts/primavera/eady_growth_rate/eady_growth_rate.py +++ b/esmvaltool/diag_scripts/primavera/eady_growth_rate/eady_growth_rate.py @@ -11,19 +11,24 @@ import matplotlib.pyplot as plt import numpy as np from dask import array as da +from esmvalcore.preprocessor import ( + annual_statistics, + extract_levels, + regrid, + seasonal_statistics, +) import esmvaltool.diag_scripts.shared import esmvaltool.diag_scripts.shared.names as shared_names -from esmvalcore.preprocessor import (annual_statistics, extract_levels, regrid, - seasonal_statistics) from esmvaltool.diag_scripts.shared import ProvenanceLogger, group_metadata - logger = logging.getLogger(os.path.basename(__file__)) class EadyGrowthRate(object): - """Class used to compute the Eady Growth Rate.""" + """ + Class used to compute the Eady Growth Rate. + """ def __init__(self, config): """ Set diagnostic parameters and constants. @@ -39,15 +44,15 @@ def __init__(self, config): self.fill_value = 1e20 """Fill Value.""" self.ref_p = 1000.0 - """Reference Pressure [Pa]""" + """Reference Pressure [Pa].""" self.gravity = 9.80665 - """Gravity [m/s]""" + """Gravity [m/s].""" self.con = 0.3098 - """Constant""" + """Constant.""" self.omega = 7.292e-5 - """Rotation of the Earth [rad/s]""" + """Rotation of the Earth [rad/s].""" self.time_statistic = self.cfg['time_statistic'] - """Time statistic to perform""" + """Time statistic to perform.""" def compute(self): """ @@ -102,7 +107,8 @@ def compute(self): self.save(cube_egr, alias, data) def potential_temperature(self, temperature, plev): - """Computes the potential temperature. + """ + Computes the potential temperature. Parameters ---------- @@ -132,7 +138,8 @@ def potential_temperature(self, temperature, plev): return theta def vertical_integration(self, var_x, var_y): - """Perform a non-cyclic centered finite-difference to integrate + """ + Perform a non-cyclic centered finite-difference to integrate variable x with respect to variable y along pressure levels. Parameters @@ -142,8 +149,8 @@ def vertical_integration(self, var_x, var_y): y: iris.cube.Cube Cube of variable y. - Returns: - -------- + Returns + ------- dxdy: iris.cube.Cube Cube of variable integrated along pressure levels. """ @@ -179,7 +186,8 @@ def vertical_integration(self, var_x, var_y): return dxdy def brunt_vaisala_frq(self, theta, geopotential): - """Compute Brunt-Väisälä frequency. + """ + Compute Brunt-Väisälä frequency. Parameters ---------- @@ -188,8 +196,8 @@ def brunt_vaisala_frq(self, theta, geopotential): geopotential: iris.cube.Cube Cube of variable zg. - Returns: - -------- + Returns + ------- brunt: da.array Array containing Brunt-Väisälä frequency. """ @@ -202,7 +210,8 @@ def brunt_vaisala_frq(self, theta, geopotential): return brunt def coriolis(self, lats, ndim): - """Compute Coriolis force. + """ + Compute Coriolis force. Parameters ---------- @@ -211,8 +220,8 @@ def coriolis(self, lats, ndim): ndim: int Number of dimension. - Returns: - -------- + Returns + ------- fcor: da.array Array containing Coriolis force. """ @@ -223,7 +232,8 @@ def coriolis(self, lats, ndim): return fcor def eady_growth_rate(self, fcor, eastward_wind, geopotential, brunt): - """Compute Eady Growth Rate. + """ + Compute Eady Growth Rate. Parameters ---------- @@ -234,8 +244,8 @@ def eady_growth_rate(self, fcor, eastward_wind, geopotential, brunt): brunt: da.array Array containing Brunt-Väisäla frequency - Returns: - -------- + Returns + ------- egr: da.array Array containing Eady Growth Rate. """ @@ -245,7 +255,8 @@ def eady_growth_rate(self, fcor, eastward_wind, geopotential, brunt): return egr def seasonal_plots(self, egr, alias): - """Plot seasonal Eady Growth rate values. + """ + Plot seasonal Eady Growth rate values. Parameters ---------- egr: iris.cube.Cube @@ -277,25 +288,27 @@ def seasonal_plots(self, egr, alias): plt.close() def save(self, egr, alias, data): - """Save results and write provenance.""" + """ + Save results and write provenance. + """ script = self.cfg[shared_names.SCRIPT] info = data[alias][0] keys = [ str(info[key]) for key in ('project', 'dataset', 'exp', 'ensemble', 'diagnostic', 'start_year', 'end_year') if key in info - ] + ] output_name = '_'.join(keys) + '.nc' output_file = os.path.join( self.cfg[shared_names.WORK_DIR], output_name) iris.save(egr, output_file) caption = ("{script} between {start} and {end}" - "according to {dataset}").format(script=script.replace( - " ", '_'), - start=info['start_year'], - end=info['end_year'], - dataset=info['dataset']) + "according to {dataset}").format( + script=script.replace(" ", '_'), + start=info['start_year'], + end=info['end_year'], + dataset=info['dataset']) ancestors = [] for i in range(len(data[alias])): ancestors.append(data[alias][i]['filename']) @@ -311,7 +324,9 @@ def save(self, egr, alias, data): def main(): - """Run Eady Growth Rate diagnostic""" + """ + Run Eady Growth Rate diagnostic. + """ with esmvaltool.diag_scripts.shared.run_diagnostic() as config: EadyGrowthRate(config).compute() From 063d0efa4bfcf311ebba415db6b148f9e5ccd95c Mon Sep 17 00:00:00 2001 From: sloosvel <45196700+sloosvel@users.noreply.github.com> Date: Wed, 25 Nov 2020 12:10:02 +0100 Subject: [PATCH 36/47] Update doc/sphinx/source/recipes/recipe_eady_growth_rate.rst Co-authored-by: Stef Smeets --- doc/sphinx/source/recipes/recipe_eady_growth_rate.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/sphinx/source/recipes/recipe_eady_growth_rate.rst b/doc/sphinx/source/recipes/recipe_eady_growth_rate.rst index 7208a5a2ed..1f8c51361b 100644 --- a/doc/sphinx/source/recipes/recipe_eady_growth_rate.rst +++ b/doc/sphinx/source/recipes/recipe_eady_growth_rate.rst @@ -1,7 +1,7 @@ .. _recipes_eady_growth_rate: Eady growth rate -========================================================================================================================== +=========== Overview -------- From 2ae2cdd0a4e1d2acb8afac6df6d767b2466916d7 Mon Sep 17 00:00:00 2001 From: sloosvel Date: Wed, 25 Nov 2020 12:43:58 +0100 Subject: [PATCH 37/47] Fix more style issues --- .../primavera/eady_growth_rate/eady_growth_rate.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/esmvaltool/diag_scripts/primavera/eady_growth_rate/eady_growth_rate.py b/esmvaltool/diag_scripts/primavera/eady_growth_rate/eady_growth_rate.py index f912d83088..adc7aa288c 100644 --- a/esmvaltool/diag_scripts/primavera/eady_growth_rate/eady_growth_rate.py +++ b/esmvaltool/diag_scripts/primavera/eady_growth_rate/eady_growth_rate.py @@ -26,9 +26,7 @@ class EadyGrowthRate(object): - """ - Class used to compute the Eady Growth Rate. - """ + """Class used to compute the Eady Growth Rate.""" def __init__(self, config): """ Set diagnostic parameters and constants. @@ -137,7 +135,8 @@ def potential_temperature(self, temperature, plev): return theta - def vertical_integration(self, var_x, var_y): + @staticmethod + def vertical_integration(var_x, var_y): """ Perform a non-cyclic centered finite-difference to integrate variable x with respect to variable y along pressure levels. @@ -296,8 +295,7 @@ def save(self, egr, alias, data): keys = [ str(info[key]) for key in ('project', 'dataset', 'exp', 'ensemble', 'diagnostic', 'start_year', 'end_year') - if key in info - ] + if key in info] output_name = '_'.join(keys) + '.nc' output_file = os.path.join( self.cfg[shared_names.WORK_DIR], output_name) From c22825e4944188ceef1132c85dabdd2ecbc69ca7 Mon Sep 17 00:00:00 2001 From: sloosvel Date: Wed, 25 Nov 2020 12:50:56 +0100 Subject: [PATCH 38/47] Fix documentation --- doc/sphinx/source/recipes/recipe_eady_growth_rate.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/sphinx/source/recipes/recipe_eady_growth_rate.rst b/doc/sphinx/source/recipes/recipe_eady_growth_rate.rst index 1f8c51361b..9fd6b49321 100644 --- a/doc/sphinx/source/recipes/recipe_eady_growth_rate.rst +++ b/doc/sphinx/source/recipes/recipe_eady_growth_rate.rst @@ -1,7 +1,7 @@ .. _recipes_eady_growth_rate: Eady growth rate -=========== +================ Overview -------- From 1d009caed50186bf4c181642b49b8de1b151da16 Mon Sep 17 00:00:00 2001 From: sloosvel Date: Wed, 25 Nov 2020 12:57:16 +0100 Subject: [PATCH 39/47] Fix more style issues --- .../primavera/eady_growth_rate/eady_growth_rate.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/esmvaltool/diag_scripts/primavera/eady_growth_rate/eady_growth_rate.py b/esmvaltool/diag_scripts/primavera/eady_growth_rate/eady_growth_rate.py index adc7aa288c..25219c23a3 100644 --- a/esmvaltool/diag_scripts/primavera/eady_growth_rate/eady_growth_rate.py +++ b/esmvaltool/diag_scripts/primavera/eady_growth_rate/eady_growth_rate.py @@ -27,6 +27,7 @@ class EadyGrowthRate(object): """Class used to compute the Eady Growth Rate.""" + def __init__(self, config): """ Set diagnostic parameters and constants. @@ -34,7 +35,8 @@ def __init__(self, config): Parameters ---------- config : dict - Dictionary containing configuration settings + Dictionary containing configuration settings. + """ self.cfg = config From fa2b28782454c05be2a38ce2d48b990a092c1530 Mon Sep 17 00:00:00 2001 From: sloosvel <45196700+sloosvel@users.noreply.github.com> Date: Wed, 2 Dec 2020 12:23:01 +0100 Subject: [PATCH 40/47] Apply suggestions from code review Co-authored-by: Stef Smeets --- .../source/recipes/recipe_eady_growth_rate.rst | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/doc/sphinx/source/recipes/recipe_eady_growth_rate.rst b/doc/sphinx/source/recipes/recipe_eady_growth_rate.rst index 9fd6b49321..ff80dfd839 100644 --- a/doc/sphinx/source/recipes/recipe_eady_growth_rate.rst +++ b/doc/sphinx/source/recipes/recipe_eady_growth_rate.rst @@ -15,28 +15,28 @@ pressure levels. Available recipes and diagnostics --------------------------------- -Recipes are stored in esmvaltool/recipes/ +Recipes are stored in ``esmvaltool/recipes/`` - * recipe_eady_growth_rate.yml + * ``recipe_eady_growth_rate.yml`` -Diagnostics are stored in esmvaltool/diag_scripts/eady_growth_rate/ +Diagnostics are stored in ``esmvaltool/diag_scripts/eady_growth_rate/`` - * eady_growth_rate.py: Computes and stores the eady growth rate. + * ``eady_growth_rate.py``: Computes and stores the eady growth rate. Plots can be produced for the seasonal mean over the North Atlantic region. User settings in recipe ----------------------- -#. Script eady_growth_rate.py +#. Script ``eady_growth_rate.py`` *Required settings for script* - * time_statistic: Set to 'annual' to compute the annual mean. Set to 'seasonal' to compute the seasonal mean. + * ``time_statistic``: Set to `'annual'` to compute the annual mean. Set to `'seasonal'` to compute the seasonal mean. *Optional settings for script* - * plot_levels: list of pressure levels to be plotted for the seasonal mean. If not specified, all levels will be plotted. + * ``plot_levels``: list of pressure levels to be plotted for the seasonal mean. If not specified, all levels will be plotted. Variables From 8c4f27a75d755bbcf40acf52021a1c3ef306b4df Mon Sep 17 00:00:00 2001 From: sloosvel Date: Wed, 9 Dec 2020 15:36:41 +0100 Subject: [PATCH 41/47] Fix more style issues --- .../eady_growth_rate/eady_growth_rate.py | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/esmvaltool/diag_scripts/primavera/eady_growth_rate/eady_growth_rate.py b/esmvaltool/diag_scripts/primavera/eady_growth_rate/eady_growth_rate.py index 25219c23a3..d88a29ef54 100644 --- a/esmvaltool/diag_scripts/primavera/eady_growth_rate/eady_growth_rate.py +++ b/esmvaltool/diag_scripts/primavera/eady_growth_rate/eady_growth_rate.py @@ -1,3 +1,4 @@ +"""Diagnostic for PRIMAVERA Eady Growth Rate""" import logging import os import sys @@ -18,14 +19,16 @@ seasonal_statistics, ) -import esmvaltool.diag_scripts.shared -import esmvaltool.diag_scripts.shared.names as shared_names -from esmvaltool.diag_scripts.shared import ProvenanceLogger, group_metadata - +from esmvaltool.diag_scripts.shared import ( + group_metadata, + names, + ProvenanceLogger, + run_diagnostic +) logger = logging.getLogger(os.path.basename(__file__)) -class EadyGrowthRate(object): +class EadyGrowthRate: """Class used to compute the Eady Growth Rate.""" def __init__(self, config): @@ -36,11 +39,9 @@ def __init__(self, config): ---------- config : dict Dictionary containing configuration settings. - """ self.cfg = config - self.filenames = esmvaltool.diag_scripts.shared.Datasets(self.cfg) self.fill_value = 1e20 """Fill Value.""" self.ref_p = 1000.0 @@ -285,14 +286,14 @@ def seasonal_plots(self, egr, alias): plotname = '_'.join([alias, diagnostic, str(int(level))]) + f'.{extension}' plt.savefig(os.path.join( - self.cfg[shared_names.PLOT_DIR], plotname)) + self.cfg[names.PLOT_DIR], plotname)) plt.close() def save(self, egr, alias, data): """ Save results and write provenance. """ - script = self.cfg[shared_names.SCRIPT] + script = self.cfg[names.SCRIPT] info = data[alias][0] keys = [ str(info[key]) for key in ('project', 'dataset', 'exp', 'ensemble', @@ -300,7 +301,7 @@ def save(self, egr, alias, data): if key in info] output_name = '_'.join(keys) + '.nc' output_file = os.path.join( - self.cfg[shared_names.WORK_DIR], output_name) + self.cfg[names.WORK_DIR], output_name) iris.save(egr, output_file) caption = ("{script} between {start} and {end}" @@ -327,7 +328,7 @@ def main(): """ Run Eady Growth Rate diagnostic. """ - with esmvaltool.diag_scripts.shared.run_diagnostic() as config: + with run_diagnostic() as config: EadyGrowthRate(config).compute() From fd5e1f0561f627c9594a506cd0b0c717e065fd74 Mon Sep 17 00:00:00 2001 From: sloosvel Date: Thu, 10 Dec 2020 10:19:28 +0100 Subject: [PATCH 42/47] More codacy issues --- .../primavera/eady_growth_rate/eady_growth_rate.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/esmvaltool/diag_scripts/primavera/eady_growth_rate/eady_growth_rate.py b/esmvaltool/diag_scripts/primavera/eady_growth_rate/eady_growth_rate.py index d88a29ef54..cbc00f9cdb 100644 --- a/esmvaltool/diag_scripts/primavera/eady_growth_rate/eady_growth_rate.py +++ b/esmvaltool/diag_scripts/primavera/eady_growth_rate/eady_growth_rate.py @@ -1,4 +1,4 @@ -"""Diagnostic for PRIMAVERA Eady Growth Rate""" +"""Diagnostic for PRIMAVERA Eady Growth Rate.""" import logging import os import sys @@ -32,8 +32,7 @@ class EadyGrowthRate: """Class used to compute the Eady Growth Rate.""" def __init__(self, config): - """ - Set diagnostic parameters and constants. + """Set diagnostic parameters and constants. Parameters ---------- From 35169859f8031941071a5448c0fe5bed878cec19 Mon Sep 17 00:00:00 2001 From: sloosvel Date: Mon, 15 Feb 2021 12:27:00 +0100 Subject: [PATCH 43/47] Add suggestion fro scientific review --- .../recipes/recipe_eady_growth_rate.rst | 6 +++++- .../eady_growth_rate/eady_growth_rate.py | 8 +++----- .../recipes/recipe_eady_growth_rate.yml | 20 +++---------------- 3 files changed, 11 insertions(+), 23 deletions(-) diff --git a/doc/sphinx/source/recipes/recipe_eady_growth_rate.rst b/doc/sphinx/source/recipes/recipe_eady_growth_rate.rst index ff80dfd839..15d492d8ba 100644 --- a/doc/sphinx/source/recipes/recipe_eady_growth_rate.rst +++ b/doc/sphinx/source/recipes/recipe_eady_growth_rate.rst @@ -6,7 +6,7 @@ Eady growth rate Overview -------- -This recipe computes the Eady growth rate and performs the annual and seasonal means, storing +This recipe computes the maximum Eady Growth Rate and performs the annual and seasonal means, storing the results for each dataset. For the seasonal means, the results are plotted over the North-Atlantic region for the selected pressure levels. @@ -46,6 +46,10 @@ Variables * zg (atmos, monthly mean, longitude latitude level time) * ua (atmos, monthly mean, longitude latitude level time) +References +---------- + +Brian J Hoskins and Paul J Valdes. On the existence of storm-tracks. Journal of the atmospheric sciences, 47(15):1854–1864, 1990.) Example plots ------------- diff --git a/esmvaltool/diag_scripts/primavera/eady_growth_rate/eady_growth_rate.py b/esmvaltool/diag_scripts/primavera/eady_growth_rate/eady_growth_rate.py index cbc00f9cdb..3d9d6c9696 100644 --- a/esmvaltool/diag_scripts/primavera/eady_growth_rate/eady_growth_rate.py +++ b/esmvaltool/diag_scripts/primavera/eady_growth_rate/eady_growth_rate.py @@ -27,7 +27,6 @@ ) logger = logging.getLogger(os.path.basename(__file__)) - class EadyGrowthRate: """Class used to compute the Eady Growth Rate.""" @@ -39,7 +38,6 @@ def __init__(self, config): config : dict Dictionary containing configuration settings. """ - self.cfg = config self.fill_value = 1e20 """Fill Value.""" @@ -56,8 +54,7 @@ def __init__(self, config): def compute(self): """ - Computes the Eady Growth Rate and - either it's annual or the seasonal mean. + Computes the Eady Growth Rate and either it's annual or seasonal mean. """ data = group_metadata(self.cfg['input_data'].values(), 'alias') for alias in data: @@ -279,7 +276,8 @@ def seasonal_plots(self, egr, alias): # North Atlantic axes.set_extent((-90.0, 30.0, 20.0, 80.0), crs=crs_latlon) axes.set_yticks(np.linspace(25, 75, 6)) - qplt.contourf(cube, levels=np.arange(0, np.max(cube.data), 0.05)) + # Relevant range + qplt.contourf(cube, levels=np.arange(0, 1.1, 0.05)) extension = self.cfg['output_file_type'] diagnostic = self.cfg['script'] plotname = '_'.join([alias, diagnostic, diff --git a/esmvaltool/recipes/recipe_eady_growth_rate.yml b/esmvaltool/recipes/recipe_eady_growth_rate.yml index 832e6f2d26..7e3be8ca14 100644 --- a/esmvaltool/recipes/recipe_eady_growth_rate.yml +++ b/esmvaltool/recipes/recipe_eady_growth_rate.yml @@ -2,7 +2,9 @@ --- documentation: description: | - Recipe to compute the annual mean or the seasonal mean the Eady Growth Rate (EGR). + Recipe to compute the annual mean or the seasonal mean of the maximum Eady Growth Rate + (EGR, Brian J Hoskins and Paul J Valdes. On the existence of storm-tracks. + Journal of the atmospheric sciences, 47(15):1854–1864, 1990.). The output produces netcdf files for each model. In the case of the seasonal means, a plot is produced for each specified level showing the EGR values over the North-Atlantic region. @@ -18,26 +20,10 @@ documentation: - primavera datasets: - - {dataset: CMCC-CM2-HR4, project: CMIP6, exp: highresSST-present, - ensemble: r1i1p1f1, mip: Amon, grid: gn, start_year: 1950, end_year: 2014} - - {dataset: CNRM-CM6-1, project: CMIP6, exp: highresSST-present, - ensemble: r1i1p1f2, mip: Amon, grid: gr, start_year: 1950, end_year: 2014} - - {dataset: CNRM-CM6-1-HR, project: CMIP6, exp: highresSST-present, - ensemble: r1i1p1f2, mip: Amon, grid: gr, start_year: 1950, end_year: 2014} - - {dataset: ECMWF-IFS-HR, project: CMIP6, exp: highresSST-present, - ensemble: r1i1p1f1, mip: Amon, grid: gr, start_year: 1950, end_year: 2014} - - {dataset: ECMWF-IFS-LR, project: CMIP6, exp: highresSST-present, - ensemble: r1i1p1f1, mip: Amon, grid: gr, start_year: 1950, end_year: 2014} - - {dataset: HadGEM3-GC31-HM, project: CMIP6, exp: highresSST-present, - ensemble: r1i1p1f1, mip: Amon, grid: gn, start_year: 1950, end_year: 2014} - {dataset: HadGEM3-GC31-LM, project: CMIP6, exp: highresSST-present, ensemble: r1i1p1f1, mip: Amon, grid: gn, start_year: 1950, end_year: 2014} - {dataset: HadGEM3-GC31-MM, project: CMIP6, exp: highresSST-present, ensemble: r1i1p1f1, mip: Amon, grid: gn, start_year: 1950, end_year: 2014} - - {dataset: MPI-ESM1-2-XR, project: CMIP6, exp: highresSST-present, - ensemble: r1i1p1f1, mip: Amon, grid: gn, start_year: 1950, end_year: 2014} - - {dataset: MPI-ESM1-2-HR, project: CMIP6, exp: highresSST-present, - ensemble: r1i1p1f1, mip: Amon, grid: gn, start_year: 1950, end_year: 2014} preprocessors: From 3b81d945dba76ab67685e8bd69ba1380c0e67914 Mon Sep 17 00:00:00 2001 From: sloosvel Date: Mon, 15 Feb 2021 12:55:27 +0100 Subject: [PATCH 44/47] Codacy --- .../eady_growth_rate/eady_growth_rate.py | 89 ++++++++----------- 1 file changed, 37 insertions(+), 52 deletions(-) diff --git a/esmvaltool/diag_scripts/primavera/eady_growth_rate/eady_growth_rate.py b/esmvaltool/diag_scripts/primavera/eady_growth_rate/eady_growth_rate.py index 3d9d6c9696..ebb1e94ce5 100644 --- a/esmvaltool/diag_scripts/primavera/eady_growth_rate/eady_growth_rate.py +++ b/esmvaltool/diag_scripts/primavera/eady_growth_rate/eady_growth_rate.py @@ -20,16 +20,17 @@ ) from esmvaltool.diag_scripts.shared import ( + ProvenanceLogger, group_metadata, names, - ProvenanceLogger, - run_diagnostic + run_diagnostic, ) + logger = logging.getLogger(os.path.basename(__file__)) + class EadyGrowthRate: """Class used to compute the Eady Growth Rate.""" - def __init__(self, config): """Set diagnostic parameters and constants. @@ -53,9 +54,8 @@ def __init__(self, config): """Time statistic to perform.""" def compute(self): - """ - Computes the Eady Growth Rate and either it's annual or seasonal mean. - """ + """Computes the Eady Growth Rate and either it's annual or seasonal + mean.""" data = group_metadata(self.cfg['input_data'].values(), 'alias') for alias in data: var = group_metadata(data[alias], 'short_name') @@ -75,11 +75,12 @@ def compute(self): eastward_wind = iris.load_cube(var['ua'][0]['filename']) if eastward_wind.shape is not geopotential.shape: - eastward_wind = regrid( - eastward_wind, geopotential, scheme='linear') + eastward_wind = regrid(eastward_wind, + geopotential, + scheme='linear') - egr = self.eady_growth_rate( - fcor, eastward_wind, geopotential, brunt) + egr = self.eady_growth_rate(fcor, eastward_wind, geopotential, + brunt) cube_egr = eastward_wind.copy(egr * 86400) @@ -104,8 +105,7 @@ def compute(self): self.save(cube_egr, alias, data) def potential_temperature(self, temperature, plev): - """ - Computes the potential temperature. + """Computes the potential temperature. Parameters ---------- @@ -118,17 +118,13 @@ def potential_temperature(self, temperature, plev): ------- theta: iris.cube.Cube Cube of potential temperature theta. - """ reference_pressure = iris.coords.AuxCoord( - self.ref_p, - long_name='reference_pressure', - units='hPa') + self.ref_p, long_name='reference_pressure', units='hPa') reference_pressure.convert_units(plev.units) pressure = (reference_pressure.points / plev.points)**(2 / 7) theta = temperature * iris.util.broadcast_to_shape( - pressure, - temperature.shape, + pressure, temperature.shape, temperature.coord_dims('air_pressure')) theta.long_name = 'potential_air_temperature' @@ -136,8 +132,7 @@ def potential_temperature(self, temperature, plev): @staticmethod def vertical_integration(var_x, var_y): - """ - Perform a non-cyclic centered finite-difference to integrate + """Perform a non-cyclic centered finite-difference to integrate variable x with respect to variable y along pressure levels. Parameters @@ -159,17 +154,15 @@ def vertical_integration(var_x, var_y): (var_x[:, 1, :, :].lazy_data() - var_x[:, 0, :, :].lazy_data()) / (var_y[:, 1, :, :].lazy_data() - var_y[:, 0, :, :].lazy_data())) - dxdy_centre = ( - (var_x[:, 2:plevs, :, :].lazy_data() - - var_x[:, 0:plevs - 2, :, :].lazy_data()) / - (var_y[:, 2:plevs, :, :].lazy_data() - - var_y[:, 0:plevs - 2, :, :].lazy_data())) + dxdy_centre = ((var_x[:, 2:plevs, :, :].lazy_data() - + var_x[:, 0:plevs - 2, :, :].lazy_data()) / + (var_y[:, 2:plevs, :, :].lazy_data() - + var_y[:, 0:plevs - 2, :, :].lazy_data())) - dxdy_end = ( - (var_x[:, plevs - 1, :, :].lazy_data() - - var_x[:, plevs - 2, :, :].lazy_data()) / - (var_y[:, plevs - 1, :, :].lazy_data() - - var_y[:, plevs - 2, :, :].lazy_data())) + dxdy_end = ((var_x[:, plevs - 1, :, :].lazy_data() - + var_x[:, plevs - 2, :, :].lazy_data()) / + (var_y[:, plevs - 1, :, :].lazy_data() - + var_y[:, plevs - 2, :, :].lazy_data())) bounds = [dxdy_end, dxdy_0] stacked_bounds = da.stack(bounds, axis=1) @@ -184,8 +177,7 @@ def vertical_integration(var_x, var_y): return dxdy def brunt_vaisala_frq(self, theta, geopotential): - """ - Compute Brunt-Väisälä frequency. + """Compute Brunt-Väisälä frequency. Parameters ---------- @@ -208,8 +200,7 @@ def brunt_vaisala_frq(self, theta, geopotential): return brunt def coriolis(self, lats, ndim): - """ - Compute Coriolis force. + """Compute Coriolis force. Parameters ---------- @@ -230,8 +221,7 @@ def coriolis(self, lats, ndim): return fcor def eady_growth_rate(self, fcor, eastward_wind, geopotential, brunt): - """ - Compute Eady Growth Rate. + """Compute Eady Growth Rate. Parameters ---------- @@ -282,31 +272,28 @@ def seasonal_plots(self, egr, alias): diagnostic = self.cfg['script'] plotname = '_'.join([alias, diagnostic, str(int(level))]) + f'.{extension}' - plt.savefig(os.path.join( - self.cfg[names.PLOT_DIR], plotname)) + plt.savefig(os.path.join(self.cfg[names.PLOT_DIR], plotname)) plt.close() def save(self, egr, alias, data): - """ - Save results and write provenance. - """ + """Save results and write provenance.""" script = self.cfg[names.SCRIPT] info = data[alias][0] keys = [ str(info[key]) for key in ('project', 'dataset', 'exp', 'ensemble', 'diagnostic', 'start_year', 'end_year') - if key in info] + if key in info + ] output_name = '_'.join(keys) + '.nc' - output_file = os.path.join( - self.cfg[names.WORK_DIR], output_name) + output_file = os.path.join(self.cfg[names.WORK_DIR], output_name) iris.save(egr, output_file) caption = ("{script} between {start} and {end}" - "according to {dataset}").format( - script=script.replace(" ", '_'), - start=info['start_year'], - end=info['end_year'], - dataset=info['dataset']) + "according to {dataset}").format(script=script.replace( + " ", '_'), + start=info['start_year'], + end=info['end_year'], + dataset=info['dataset']) ancestors = [] for i in range(len(data[alias])): ancestors.append(data[alias][i]['filename']) @@ -322,9 +309,7 @@ def save(self, egr, alias, data): def main(): - """ - Run Eady Growth Rate diagnostic. - """ + """Run Eady Growth Rate diagnostic.""" with run_diagnostic() as config: EadyGrowthRate(config).compute() From 17a3f0caeaff225621264178a9d2abdaa33278f9 Mon Sep 17 00:00:00 2001 From: sloosvel Date: Mon, 15 Feb 2021 13:16:41 +0100 Subject: [PATCH 45/47] Remove uneeded parenthesis --- doc/sphinx/source/recipes/recipe_eady_growth_rate.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/sphinx/source/recipes/recipe_eady_growth_rate.rst b/doc/sphinx/source/recipes/recipe_eady_growth_rate.rst index 15d492d8ba..afad17703f 100644 --- a/doc/sphinx/source/recipes/recipe_eady_growth_rate.rst +++ b/doc/sphinx/source/recipes/recipe_eady_growth_rate.rst @@ -49,7 +49,7 @@ Variables References ---------- -Brian J Hoskins and Paul J Valdes. On the existence of storm-tracks. Journal of the atmospheric sciences, 47(15):1854–1864, 1990.) +Brian J Hoskins and Paul J Valdes. On the existence of storm-tracks. Journal of the atmospheric sciences, 47(15):1854–1864, 1990. Example plots ------------- From 5d02859fc2859ba460692cfc619261cb08816518 Mon Sep 17 00:00:00 2001 From: Javier Vegas-Regidor Date: Tue, 16 Feb 2021 11:11:09 +0100 Subject: [PATCH 46/47] Fix doc format --- .../eady_growth_rate/eady_growth_rate.py | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/esmvaltool/diag_scripts/primavera/eady_growth_rate/eady_growth_rate.py b/esmvaltool/diag_scripts/primavera/eady_growth_rate/eady_growth_rate.py index ebb1e94ce5..406a069ab0 100644 --- a/esmvaltool/diag_scripts/primavera/eady_growth_rate/eady_growth_rate.py +++ b/esmvaltool/diag_scripts/primavera/eady_growth_rate/eady_growth_rate.py @@ -32,7 +32,8 @@ class EadyGrowthRate: """Class used to compute the Eady Growth Rate.""" def __init__(self, config): - """Set diagnostic parameters and constants. + """ + Set diagnostic parameters and constants. Parameters ---------- @@ -54,8 +55,7 @@ def __init__(self, config): """Time statistic to perform.""" def compute(self): - """Computes the Eady Growth Rate and either it's annual or seasonal - mean.""" + """Compute Eady Growth Rate and either it's annual or seasonal mean.""" data = group_metadata(self.cfg['input_data'].values(), 'alias') for alias in data: var = group_metadata(data[alias], 'short_name') @@ -105,7 +105,7 @@ def compute(self): self.save(cube_egr, alias, data) def potential_temperature(self, temperature, plev): - """Computes the potential temperature. + """Compute potential temperature. Parameters ---------- @@ -132,7 +132,10 @@ def potential_temperature(self, temperature, plev): @staticmethod def vertical_integration(var_x, var_y): - """Perform a non-cyclic centered finite-difference to integrate + """ + Vertical integration. + + Perform a non-cyclic centered finite-difference to integrate variable x with respect to variable y along pressure levels. Parameters @@ -147,7 +150,6 @@ def vertical_integration(var_x, var_y): dxdy: iris.cube.Cube Cube of variable integrated along pressure levels. """ - plevs = var_x.shape[1] dxdy_0 = ( @@ -225,6 +227,8 @@ def eady_growth_rate(self, fcor, eastward_wind, geopotential, brunt): Parameters ---------- + fcor: da.array + Array containing Coriolis force. eastward_wind: iris.cube.Cube Cube containing variable ua. geopotential: iris.cube.Cube @@ -245,6 +249,7 @@ def eady_growth_rate(self, fcor, eastward_wind, geopotential, brunt): def seasonal_plots(self, egr, alias): """ Plot seasonal Eady Growth rate values. + Parameters ---------- egr: iris.cube.Cube @@ -288,12 +293,9 @@ def save(self, egr, alias, data): output_file = os.path.join(self.cfg[names.WORK_DIR], output_name) iris.save(egr, output_file) - caption = ("{script} between {start} and {end}" - "according to {dataset}").format(script=script.replace( - " ", '_'), - start=info['start_year'], - end=info['end_year'], - dataset=info['dataset']) + script_name = script.replace(" ", '_') + caption = (f"{script_name} between {info['start_year']} " + f"and {info['end_year']} according to {info['dataset']}") ancestors = [] for i in range(len(data[alias])): ancestors.append(data[alias][i]['filename']) From d6d1d628099e403d6a7f75e2d2bdb74bbe4de9ee Mon Sep 17 00:00:00 2001 From: Javier Vegas-Regidor Date: Tue, 16 Feb 2021 11:11:21 +0100 Subject: [PATCH 47/47] Fix doc format --- .../diag_scripts/primavera/eady_growth_rate/eady_growth_rate.py | 1 + 1 file changed, 1 insertion(+) diff --git a/esmvaltool/diag_scripts/primavera/eady_growth_rate/eady_growth_rate.py b/esmvaltool/diag_scripts/primavera/eady_growth_rate/eady_growth_rate.py index 406a069ab0..113b8a35af 100644 --- a/esmvaltool/diag_scripts/primavera/eady_growth_rate/eady_growth_rate.py +++ b/esmvaltool/diag_scripts/primavera/eady_growth_rate/eady_growth_rate.py @@ -31,6 +31,7 @@ class EadyGrowthRate: """Class used to compute the Eady Growth Rate.""" + def __init__(self, config): """ Set diagnostic parameters and constants.