1- #!/usr/bin/env python
1+ #!/usr/bin/env python3
22'''
3- Copyright 2019 ECMWF.
4-
3+ Copyright 2023 ECMWF.
4+
55This software is licensed under the terms of the Apache Licence Version 2.0
66which can be obtained at http://www.apache.org/licenses/LICENSE-2.0
7-
7+
88In applying this licence, ECMWF does not waive the privileges and immunities
99granted to it by virtue of its status as an intergovernmental organisation
1010nor does it submit to any jurisdiction.
11-
11+
1212**************************************************************************
1313Function : compute_geopotential_on_ml
14-
14+
1515Author (date) : Cristian Simarro (09/10/2015)
1616modified: Cristian Simarro (20/03/2017) - migrated to eccodes
1717 Xavi Abellan (03/12/2018) - compatibilty with Python 3
1818 Xavi Abellan (27/03/2020) - Corrected steps in output file
1919 Xavi Abellan (05/08/2020) - Better handling of levels
20-
20+ Xavi Abellan (31/01/2023) - Better handling of steps
21+
2122Category : COMPUTATION
22-
23+
2324OneLineDesc : Computes geopotential on model levels
24-
25+
2526Description : Computes geopotential on model levels.
2627 Based on code from Nils Wedi, the IFS documentation:
2728 https://software.ecmwf.int/wiki/display/IFS/CY41R1+Official+IFS+Documentation
2829 part III. Dynamics and numerical procedures
2930 optimised implementation by Dominique Lucas.
3031 ported to Python by Cristian Simarro
31-
32+
3233Parameters : tq.grib - grib file with all the levelist
3334 of t and q
3435 zlnsp.grib - grib file with levelist 1 for params
3738 to store in the output
3839 -o output (optional) - name of the output file
3940 (default='z_out.grib')
40-
41+
4142Return Value : output (default='z_out.grib')
4243 A fieldset of geopotential on model levels
43-
44+
4445Dependencies : None
45-
46+
4647Example Usage :
4748 compute_geopotential_on_ml.py tq.grib zlnsp.grib
4849'''
5556 codes_index_add_file , codes_get_array , codes_get_values ,
5657 codes_index_release , codes_release , codes_set_values ,
5758 codes_write )
58-
59+
5960R_D = 287.06
6061R_G = 9.80665
61-
62-
62+
63+
6364def parse_args ():
6465 ''' Parse program arguments using ArgumentParser'''
6566 parser = argparse .ArgumentParser (
@@ -89,66 +90,63 @@ def parse_args():
8990 args .levelist = list (range (int (args .levelist [0 ]),
9091 int (args .levelist [2 ]) + 1 ))
9192 else :
92- args .levelist = [int (_l ) for _l in args .levelist .split ('/' )]
93+ args .levelist = [int (l ) for l in args .levelist .split ('/' )]
9394 return args
94-
95-
95+
96+
9697def main ():
9798 '''Main function'''
9899 args = parse_args ()
99-
100+
100101 print ('Arguments: %s' % ", " .join (
101102 ['%s: %s' % (k , v ) for k , v in vars (args ).items ()]))
102-
103+
103104 fout = open (args .output , 'wb' )
104105 index_keys = ['date' , 'time' , 'shortName' , 'level' , 'step' ]
105-
106+
106107 idx = codes_index_new_from_file (args .z_lnsp , index_keys )
107108 codes_index_add_file (idx , args .t_q )
108109 if 'u_v' in args :
109110 codes_index_add_file (idx , args .u_v )
110-
111+ values = None
111112 # iterate date
112113 for date in codes_index_get (idx , 'date' ):
113114 codes_index_select (idx , 'date' , date )
114- # iterate step
115+ # iterate time
115116 for time in codes_index_get (idx , 'time' ):
116117 codes_index_select (idx , 'time' , time )
117- values = None
118- # iterate step all but geopotential z which is always step 0 (an)
119118 for step in codes_index_get (idx , 'step' ):
120- if values is None :
121- values = get_initial_values (idx , step = step , keep_sample = True )
122- if 'height' in args :
123- values ['height' ] = args .height
124- values ['gh' ] = args .height * R_G + values ['z' ]
125- if 'levelist' in args :
126- values ['levelist' ] = args .levelist
127119 codes_index_select (idx , 'step' , step )
128- # surface pressure
129- try :
130- values ['sp' ] = get_surface_pressure (idx )
131- production_step (idx , step , values , fout )
132- except WrongStepError :
133- if step != '0' :
134- raise
135-
120+ if not values :
121+ values = get_initial_values (idx , keep_sample = True )
122+ if 'height' in args :
123+ values ['height' ] = args .height
124+ values ['gh' ] = args .height * R_G + values ['z' ]
125+ if 'levelist' in args :
126+ values ['levelist' ] = args .levelist
127+ # surface pressure
128+ try :
129+ values ['sp' ] = get_surface_pressure (idx )
130+ production_step (idx , step , values , fout )
131+ except WrongStepError :
132+ if step != '0' :
133+ raise
134+
136135 try :
137136 codes_release (values ['sample' ])
138137 except KeyError :
139138 pass
140-
139+
141140 codes_index_release (idx )
142141 fout .close ()
143-
144-
145- def get_initial_values (idx , step , keep_sample = False ):
142+
143+
144+ def get_initial_values (idx , keep_sample = False ):
146145 '''Get the values of surface z, pv and number of levels '''
147146 codes_index_select (idx , 'level' , 1 )
148- codes_index_select (idx , 'step' , step )
149147 codes_index_select (idx , 'shortName' , 'z' )
150148 gid = codes_new_from_index (idx )
151-
149+
152150 values = {}
153151 # surface geopotential
154152 values ['z' ] = codes_get_values (gid )
@@ -160,8 +158,8 @@ def get_initial_values(idx, step, keep_sample=False):
160158 else :
161159 codes_release (gid )
162160 return values
163-
164-
161+
162+
165163def check_max_level (idx , values ):
166164 '''Make sure we have all the levels required'''
167165 # how many levels are we computing?
@@ -171,8 +169,8 @@ def check_max_level(idx, values):
171169 (sys .argv [0 ], values ['nlevels' ], max_level ),
172170 file = sys .stderr )
173171 values ['nlevels' ] = max_level
174-
175-
172+
173+
176174def get_surface_pressure (idx ):
177175 '''Get the surface pressure for date-time-step'''
178176 codes_index_select (idx , 'level' , 1 )
@@ -188,17 +186,17 @@ def get_surface_pressure(idx):
188186 sfc_p = np .exp (codes_get_values (gid ))
189187 codes_release (gid )
190188 return sfc_p
191-
192-
189+
190+
193191def get_ph_levs (values , level ):
194192 '''Return the presure at a given level and the next'''
195193 a_coef = values ['pv' ][0 :values ['nlevels' ] + 1 ]
196194 b_coef = values ['pv' ][values ['nlevels' ] + 1 :]
197195 ph_lev = a_coef [level - 1 ] + (b_coef [level - 1 ] * values ['sp' ])
198196 ph_levplusone = a_coef [level ] + (b_coef [level ] * values ['sp' ])
199197 return ph_lev , ph_levplusone
200-
201-
198+
199+
202200def compute_z_level (idx , lev , values , z_h ):
203201 '''Compute z at half & full level for the given level, based on t/q/sp'''
204202 # select the levelist and retrieve the vaules of t and q
@@ -214,38 +212,37 @@ def compute_z_level(idx, lev, values, z_h):
214212 codes_index_select (idx , 'shortName' , 'q' )
215213 gid = codes_new_from_index (idx )
216214 if gid is None :
217- print ('Q at level {} missing from input' .format (lev ))
218- else :
219- q_level = codes_get_values (gid )
220- codes_release (gid )
221-
222- # compute moist temperature
223- t_level = t_level * (1. + 0.609133 * q_level )
224-
215+ raise MissingLevelError ('Q at level {} missing from input' .format (lev ))
216+ q_level = codes_get_values (gid )
217+ codes_release (gid )
218+
219+ # compute moist temperature
220+ t_level = t_level * (1. + 0.609133 * q_level )
221+
225222 # compute the pressures (on half-levels)
226223 ph_lev , ph_levplusone = get_ph_levs (values , lev )
227-
224+
228225 if lev == 1 :
229226 dlog_p = np .log (ph_levplusone / 0.1 )
230227 alpha = np .log (2 )
231228 else :
232229 dlog_p = np .log (ph_levplusone / ph_lev )
233230 alpha = 1. - ((ph_lev / (ph_levplusone - ph_lev )) * dlog_p )
234-
231+
235232 t_level = t_level * R_D
236-
233+
237234 # z_f is the geopotential of this full level
238235 # integrate from previous (lower) half-level z_h to the
239236 # full level
240237 z_f = z_h + (t_level * alpha )
241-
238+
242239 # z_h is the geopotential of 'half-levels'
243240 # integrate z_h to next half level
244241 z_h = z_h + (t_level * dlog_p )
245-
242+
246243 return z_h , z_f
247-
248-
244+
245+
249246def production_step (idx , step , values , fout ):
250247 '''Compute z at half & full level for the given level, based on t/q/sp'''
251248 # We want to integrate up into the atmosphere, starting at the
@@ -254,10 +251,10 @@ def production_step(idx, step, values, fout):
254251 # See the IFS documentation, part III
255252 # For speed and file I/O, we perform the computations with
256253 # numpy vectors instead of fieldsets.
257-
254+
258255 z_h = values ['z' ]
259256 codes_set (values ['sample' ], 'step' , int (step ))
260-
257+
261258 for lev in sorted (values ['levelist' ], reverse = True ):
262259 try :
263260 z_h , z_f = compute_z_level (idx , lev , values , z_h )
@@ -268,17 +265,17 @@ def production_step(idx, step, values, fout):
268265 except MissingLevelError as e :
269266 print ('%s [WARN] %s' % (sys .argv [0 ], e ),
270267 file = sys .stderr )
271-
272-
268+
269+
273270class WrongStepError (Exception ):
274271 ''' Exception capturing wrong step'''
275272 pass
276-
277-
273+
274+
278275class MissingLevelError (Exception ):
279276 ''' Exception capturing missing levels in input'''
280277 pass
281-
282-
278+
279+
283280if __name__ == '__main__' :
284281 main ()
0 commit comments