Skip to content

Commit c575488

Browse files
authored
Update compute_geopotential_on_ml.py
fixes issue #35
1 parent b6fd994 commit c575488

File tree

1 file changed

+75
-78
lines changed

1 file changed

+75
-78
lines changed

bin/compute_geopotential_on_ml.py

Lines changed: 75 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,35 @@
1-
#!/usr/bin/env python
1+
#!/usr/bin/env python3
22
'''
3-
Copyright 2019 ECMWF.
4-
3+
Copyright 2023 ECMWF.
4+
55
This software is licensed under the terms of the Apache Licence Version 2.0
66
which can be obtained at http://www.apache.org/licenses/LICENSE-2.0
7-
7+
88
In applying this licence, ECMWF does not waive the privileges and immunities
99
granted to it by virtue of its status as an intergovernmental organisation
1010
nor does it submit to any jurisdiction.
11-
11+
1212
**************************************************************************
1313
Function : compute_geopotential_on_ml
14-
14+
1515
Author (date) : Cristian Simarro (09/10/2015)
1616
modified: 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+
2122
Category : COMPUTATION
22-
23+
2324
OneLineDesc : Computes geopotential on model levels
24-
25+
2526
Description : 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+
3233
Parameters : tq.grib - grib file with all the levelist
3334
of t and q
3435
zlnsp.grib - grib file with levelist 1 for params
@@ -37,12 +38,12 @@
3738
to store in the output
3839
-o output (optional) - name of the output file
3940
(default='z_out.grib')
40-
41+
4142
Return Value : output (default='z_out.grib')
4243
A fieldset of geopotential on model levels
43-
44+
4445
Dependencies : None
45-
46+
4647
Example Usage :
4748
compute_geopotential_on_ml.py tq.grib zlnsp.grib
4849
'''
@@ -55,11 +56,11 @@
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+
5960
R_D = 287.06
6061
R_G = 9.80665
61-
62-
62+
63+
6364
def 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+
9697
def 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+
165163
def 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+
176174
def 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+
193191
def 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+
202200
def 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+
249246
def 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+
273270
class WrongStepError(Exception):
274271
''' Exception capturing wrong step'''
275272
pass
276-
277-
273+
274+
278275
class MissingLevelError(Exception):
279276
''' Exception capturing missing levels in input'''
280277
pass
281-
282-
278+
279+
283280
if __name__ == '__main__':
284281
main()

0 commit comments

Comments
 (0)