Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Code Block
languagepy
titlecompute_geopotential_on_ml.py
#!/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()