Page History
...
Code Block | ||||
---|---|---|---|---|
| ||||
#!/usr/bin/env python ''' Copyright 2019 ECMWF. This software is licensed under the terms of the Apache Licence Version 2.0 which can be obtained at http://www.apache.org/licenses/LICENSE-2.0 In applying this licence, ECMWF does not waive the privileges and immunities granted to it by virtue of its status as an intergovernmental organisation nor does it submit to any jurisdiction. ************************************************************************** Function : compute_geopotential_on_ml Author (date) : Cristian Simarro (09/10/2015) modified: Cristian Simarro (20/03/2017) - migrated to eccodes Xavi Abellan (03/12/2018) - compatibilty with Python 3 Xavi Abellan (27/03/2020) - Corrected steps in output file Xavi Abellan (05/08/2020) - Better handling of levels Category : COMPUTATION OneLineDesc : Computes geopotential on model levels Description : Computes geopotential on model levels. Based on code from Nils Wedi, the IFS documentation: https://softwareconfluence.ecmwf.int/wiki/display/IFS/CY41R1+Official+IFS+Documentation part III. Dynamics and numerical procedures optimised implementation by Dominique Lucas. ported to Python by Cristian Simarro Parameters : tq.grib - grib file with all the levelist of t and q zlnsp.grib - grib file with levelist 1 for params z and lnsp -l levelist (optional) - slash '/' separated list of levelist to store in the output -o output (optional) - name of the output file (default='z_out.grib') Return Value : output (default='z_out.grib') A fieldset of geopotential on model levels Dependencies : None Example Usage : compute_geopotential_on_ml.py tq.grib zlnsp.grib ''' from __future__ import print_function import sys import argparse import numpy as np from eccodes import (codes_index_new_from_file, codes_index_get, codes_get, codes_index_select, codes_new_from_index, codes_set, codes_index_add_file, codes_get_array, codes_get_values, codes_index_release, codes_release, codes_set_values, codes_write) R_D = 287.06 R_G = 9.80665 def parse_args(): ''' Parse program arguments using ArgumentParser''' parser = argparse.ArgumentParser( description='Python tool to calculate the Z of the model levels') parser.add_argument('-l', '--levelist', help='levelist to store', default='all') parser.add_argument('-o', '--output', help='name of the output file', default='z_out.grib') parser.add_argument('t_q', metavar='tq.grib', type=str, help=('grib file with temperature(t) and humidity(q)', 'for the model levels')) parser.add_argument('z_lnsp', metavar='zlnsp.grib', type=str, help=('grib file with geopotential(z) and Logarithm', 'of surface pressure(lnsp) for the ml=1')) args = parser.parse_args() # Handle levelist posibilities if args.levelist == 'all': args.levelist = '' range(1, 138) return args def mainelif "to" in args.levelist.lower(): '''Main function''' if args"by" =in parse_args.levelist.lower() : print('Arguments: %s' % ", ".join(args.levelist = args.levelist.split('/') ['%s: %s' % (k, v) for k, v in vars(args).items()])) args.levelist = list(range(int(args.levelist[0]), fout = open(args.output, 'wb') index_keys = ['date', 'time', 'shortName', 'level', 'step'] idx = codes_index_new_from_file(args.z_lnsp, index_keys) codes_index_add_file(idx, args.t_q)int(args.levelist[2]) + 1, if 'u_v' in args: codes_index_add_file(idx, args.u_v) # iterate date for date in codes_index_get(idx, 'date'): codes_index_select(idx, 'date', dateint(args.levelist[4]))) #else: iterate step for time in codes_index_get(idx, 'time'):args.levelist = args.levelist.split('/') codes_index_select(idx, 'time', time)args.levelist = list(range(int(args.levelist[0]), values = get_initial_values(idx, keep_sample=True) if 'height' in args: values['height'] = args.heightint(args.levelist[2]) + 1)) else: args.levelist = [int(l) values['gh'] =for l in args.height * R_G + values['z'.levelist.split('/')] return args def main(): '''Main if 'levelist' in args:function''' args = parse_args() print('Arguments: %s' % ", ".join( values['levelist%s: %s'] = args.levelist % (k, v) for k, v in vars(args).items()])) fout # iterate step all but geopotential z which is always step 0 (an)= open(args.output, 'wb') index_keys = ['date', 'time', 'shortName', 'level', 'step'] idx = codes_index_new_from_file(args.z_lnsp, index_keys) for step in codes_index_getadd_file(idx, 'step'):args.t_q) if 'u_v' in args: codes_index_selectadd_file(idx, 'step', step)args.u_v) # iterate date for date # surface pressurein codes_index_get(idx, 'date'): codes_index_select(idx, try:'date', date) # iterate step for values['sp'] = get_surface_pressure(idx)time in codes_index_get(idx, 'time'): production_stepcodes_index_select(idx, step'time', values, fouttime) values = except WrongStepError: get_initial_values(idx, keep_sample=True) if step != '0''height' in args: values['height'] = args.height raise try: codes_release(values['sample']) values['gh'] = args.height * R_G + values['z'] except KeyError if 'levelist' in args: pass values['levelist'] codes_index_release(idx)= args.levelist fout.close() def get_initial_values(idx, keep_sample=False): # '''Get the values of surface z, pv and number of levels ''' iterate step all but geopotential z which is always step 0 (an) for step in codes_index_selectget(idx, 'level', 1) step'): codes_index_select(idx, 'step', 0) codes_index_select(idx, 'shortNamestep', 'z'step) gid = codes_new_from_index(idx) values = {} # surface geopotentialpressure values['z'] = codes_get_values(gid) values['pv'] = codes_get_array(gid, 'pv') try: values['nlevels'] = codes_get(gid, 'NV', int) // 2 - 1 check_max_level(idx, values values['sp'] = get_surface_pressure(idx) if keep_sample: values['sample'] = gid else: production_step(idx, step, values, fout) codes_release(gid) return values def check_max_level(idx, values) except WrongStepError: '''Make sure we have all the levels required''' # how many levels are weif computing? step != '0': max_level = max(codes_index_get(idx, 'level', int)) if max_level != values['nlevels']: print('%s [WARN] total levelsraise should be: %d but it is %d' %try: (sys.argv[0], codes_release(values['nlevelssample'], max_level), except KeyError: file=sys.stderr) pass values['nlevels'] = max_levelcodes_index_release(idx) fout.close() def get_surfaceinitial_pressurevalues(idx, keep_sample=False): '''Get the values of surface pressure for date-time-step z, pv and number of levels ''' codes_index_select(idx, 'level', 1) codes_index_select(idx, 'step', 0) codes_index_select(idx, 'shortName', 'lnspz') gid = codes_new_from_index(idx) ifvalues gid is None:= {} # surface geopotential raise WrongStepError() values['z'] = if codes_get_values(gid, 'gridType', str) == 'sh':) values['pv'] = codes_get_array(gid, 'pv') values['nlevels'] = print('%s [ERROR] fields must be gridded, not spectral' % sys.argv[0],codes_get(gid, 'NV', int) // 2 - 1 check_max_level(idx, values) if keep_sample: values['sample'] file=sys.stderr)= gid sys.exit(1)else: # surface pressure sfc_p = np.exp(codes_get_valuesrelease(gid)) codes_release(gid) return sfc_p def get_ph_levs(values, levelreturn values def check_max_level(idx, values): '''ReturnMake thesure presurewe athave aall giventhe level and the nextlevels required''' a_coef# = values['pv'][0:values['nlevels'] + 1] b_coef = values['pv'][how many levels are we computing? max_level = max(codes_index_get(idx, 'level', int)) if max_level != values['nlevels'] + 1:] ph_lev = a_coef[level - 1] + (b_coef[level - 1] * values['sp']) ph_levplusone = a_coef[level] + (b_coef[level] * values['sp']) return ph_lev, ph_levplusone def compute_z_level(idx, lev, values, z_h print('%s [WARN] total levels should be: %d but it is %d' % (sys.argv[0], values['nlevels'], max_level), file=sys.stderr) values['nlevels'] = max_level def get_surface_pressure(idx): '''ComputeGet zthe atsurface halfpressure & full level for the given level, based on t/q/sp'''for date-time-step''' codes_index_select(idx, 'level', 1) codes_index_select(idx, 'shortName', 'lnsp') # select the levelist and retrieve the vaules of t and q gid = codes_new_from_index(idx) if gid is None: # t_level: values for traise WrongStepError() # q_level: values for q codes_index_select(idxif codes_get(gid, 'levelgridType', lev)str) == 'sh': codes_index_select(idx, 'shortName', 't') print('%s [ERROR] gid = codes_new_from_index(idx) t_level = codes_get_values(gid) codes_release(gid) codes_index_select(idx, 'shortName', 'q'fields must be gridded, not spectral' % sys.argv[0], file=sys.stderr) gid = codes_new_from_index(idx sys.exit(1) q_level# surface pressure sfc_p = np.exp(codes_get_values(gid)) codes_release(gid) # compute moist temperaturereturn sfc_p def get_ph_levs(values, level): t_level = t_level * (1. + 0.609133 * q_level) '''Return the presure at a given level and the next''' #a_coef compute the pressures (on half-levels)= values['pv'][0:values['nlevels'] + 1] ph_lev, ph_levplusoneb_coef = get_ph_levs(values, lev) if lev == 1:values['pv'][values['nlevels'] + 1:] ph_lev = a_coef[level - dlog_p = np.log(ph_levplusone / 0.11] + (b_coef[level - 1] * values['sp']) ph_levplusone alpha = np.log(2) else:= a_coef[level] + (b_coef[level] * values['sp']) return dlog_p = np.log(ph_lev, ph_levplusone / ph_lev) def compute_z_level(idx, lev, values, z_h): '''Compute z at alphahalf =& 1.full - ((ph_lev / (ph_levplusone - ph_lev)) * dlog_p) level for the given level, based on t/q/sp''' t_level# =select t_levelthe * R_D # z_f is the geopotential of this full level # integrate from previous (lower) half-level z_h to the # full levellevelist and retrieve the vaules of t and q # t_level: values for t # q_level: values for q codes_index_select(idx, 'level', lev) z_f = z_h + (t_level * alpha) # z_h is the geopotential of 'half-levels'codes_index_select(idx, 'shortName', 't') gid = codes_new_from_index(idx) if gid is None: # integrate z_h to next halfraise MissingLevelError('T at level {} missing z_h = z_h + (from input'.format(lev)) t_level *= dlog_pcodes_get_values(gid) codes_release(gid) return z_h, z_f def production_stepcodes_index_select(idx, step'shortName', values, fout):'q') '''Compute z at half & full level for the given level, based on t/q/sp'''gid = codes_new_from_index(idx) if gid is None: # We want to integrate up into the atmosphere, starting at theraise MissingLevelError('Q at level {} missing from input'.format(lev)) # ground so we start at the lowest level (highest number) andq_level = codes_get_values(gid) codes_release(gid) # compute moist temperature # keep accumulating the height as we go.t_level = t_level * (1. + 0.609133 * q_level) # Seecompute the IFSpressures documentation, part III(on half-levels) # For speed and file I/O, we perform the computations with # numpy vectors instead of fieldsets. z_h = values['z'] codes_set(values['sample'], 'step', int(step)) for lev in list(reversed(list(range(1, values['nlevels'] + 1)))):ph_lev, ph_levplusone = get_ph_levs(values, lev) if lev == 1: dlog_p = np.log(ph_levplusone / 0.1) alpha = np.log(2) else: dlog_p = np.log(ph_levplusone / ph_lev) alpha = 1. - ((ph_lev / (ph_levplusone - ph_lev)) * dlog_p) t_level = t_level * R_D # z_f is the geopotential of this full level # integrate from previous (lower) half-level z_h to the # full level z_f = z_h + (t_level * alpha) # z_h is the geopotential of 'half-levels' # integrate z_h to next half level z_h = z_h + (t_level * dlog_p) return z_h, z_f def production_step(idx, step, values, fout): '''Compute z at half & full level for the given level, based on t/q/sp''' # We want to integrate up into the atmosphere, starting at the # ground so we start at the lowest level (highest number) and # keep accumulating the height as we go. # See the IFS documentation, part III # For speed and file I/O, we perform the computations with # numpy vectors instead of fieldsets. z_h = values['z'] codes_set(values['sample'], 'step', int(step)) for lev in sorted(values['levelist'], reverse=True): try: z_h, z_f = compute_z_level(idx, lev, values, z_h) # store the result (z_f) in a field and add to the output z_h, z_f = compute_z_level(idx, lev, values, z_h codes_set(values['sample'], 'level', lev) # store the result codes_set_values(values['sample'], z_f) in a field and add to the output ) if codes_write(values['levelistsample'] == '' or str(lev) in values['levelist'], fout) except MissingLevelError as e: codes_set(values['sample'], 'level', lev) print('%s [WARN] %s' % (sys.argv[0], e), codes_set_values(values['sample'], z_f) file=sys.stderr) class WrongStepError(Exception): ''' Exception capturing wrong step''' codes_write(values['sample'], fout) pass class WrongStepErrorMissingLevelError(Exception): ''' Exception capturing missing wronglevels in stepinput''' pass if __name__ == '__main__': main() |