rda-python-icoads 1.0.6__py3-none-any.whl → 1.0.9__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of rda-python-icoads might be problematic. Click here for more details.
- rda_python_icoads/R3.0-stat_doc.pdf +0 -0
- rda_python_icoads/checkicoads.py +222 -0
- rda_python_icoads/cleanicoads.py +175 -0
- rda_python_icoads/counticoads.py +153 -0
- rda_python_icoads/fillicoads.py +138 -0
- rda_python_icoads/fillinventory.py +149 -0
- rda_python_icoads/fillitable.py +289 -0
- rda_python_icoads/fillmonth.py +94 -0
- rda_python_icoads/imma1_subset.py +1 -0
- rda_python_icoads/msg +457 -0
- rda_python_icoads/msg3.0_subset_readme.txt +94 -0
- rda_python_icoads/msg3_subset.py +345 -0
- rda_python_icoads/msg_download.py +211 -0
- rda_python_icoads/msgsubset.f +612 -0
- rda_python_icoads/writeicoads.py +169 -0
- {rda_python_icoads-1.0.6.dist-info → rda_python_icoads-1.0.9.dist-info}/METADATA +1 -1
- rda_python_icoads-1.0.9.dist-info/RECORD +25 -0
- rda_python_icoads-1.0.9.dist-info/entry_points.txt +12 -0
- rda_python_icoads-1.0.6.dist-info/RECORD +0 -11
- rda_python_icoads-1.0.6.dist-info/entry_points.txt +0 -2
- {rda_python_icoads-1.0.6.dist-info → rda_python_icoads-1.0.9.dist-info}/LICENSE +0 -0
- {rda_python_icoads-1.0.6.dist-info → rda_python_icoads-1.0.9.dist-info}/WHEEL +0 -0
- {rda_python_icoads-1.0.6.dist-info → rda_python_icoads-1.0.9.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
#
|
|
3
|
+
##################################################################################
|
|
4
|
+
#
|
|
5
|
+
# Title : msg3_subset
|
|
6
|
+
# Author : Zaihua Ji, zji@ucar.edu
|
|
7
|
+
# Date : 01/07/2020
|
|
8
|
+
# 2025-02-28 transferred to package rda_python_icoads from
|
|
9
|
+
# https://github.com/NCAR/rda-icoads.git
|
|
10
|
+
# Purpose : process ICOADS 3.0 MSG subset requests under control of dsrqst
|
|
11
|
+
# for mouthly summary data files
|
|
12
|
+
#
|
|
13
|
+
# Github : https://github.com/NCAR/rda-python-icoads.git
|
|
14
|
+
#
|
|
15
|
+
##################################################################################
|
|
16
|
+
|
|
17
|
+
import sys
|
|
18
|
+
import os
|
|
19
|
+
import re
|
|
20
|
+
from os import path as op
|
|
21
|
+
from rda_python_common import PgLOG
|
|
22
|
+
from rda_python_common import PgDBI
|
|
23
|
+
from . import PgIMMA
|
|
24
|
+
from rda_python_common import PgUtil
|
|
25
|
+
from rda_python_common import PgFile
|
|
26
|
+
from rda_python_dsrqst import PgSubset
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
MISSIDX = [22, 23, 24, 25]
|
|
30
|
+
STATNUM = ['10 14 18 22 26 30 34 38 42 46 ', '11 15 19 23 27 31 35 39 43 47 ',
|
|
31
|
+
'12 16 20 24 28 32 36 40 44 48 ', '13 17 21 25 29 33 37 41 45 49']
|
|
32
|
+
VARARRAY = {
|
|
33
|
+
# VAR : GRP IDX DESCRIPTION
|
|
34
|
+
'S' : [3, 0, 'sea surface temperature 0.01 @C'],
|
|
35
|
+
'A' : [3, 1, 'air temperature 0.01 @C'],
|
|
36
|
+
'Q' : [3, 2, 'specific humidity 0.01 g/kg'],
|
|
37
|
+
'R' : [3, 3, 'relative humidity 0.1 %'],
|
|
38
|
+
'W' : [4, 0, 'scalar wind 0.01 m/s'],
|
|
39
|
+
'U' : [4, 1, 'vector wind eastward component 0.01 m/s'],
|
|
40
|
+
'V' : [4, 2, 'vector wind northward component 0.01 m/s'],
|
|
41
|
+
'P' : [4, 3, 'sea level pressure 0.01 hPa'],
|
|
42
|
+
'C' : [5, 0, 'total cloudiness 0.1 okta'],
|
|
43
|
+
'X' : [5, 2, 'WU (wind stress 0.1 m**2/s**2'],
|
|
44
|
+
'Y' : [5, 3, 'WV parameters) 0.1 m**2/s**2'],
|
|
45
|
+
'D' : [6, 0, 'S - A = sea-air temp. diff. 0.01 @C'],
|
|
46
|
+
'E' : [6, 1, '(S - A)W 0.1 @C m/s'],
|
|
47
|
+
'F' : [6, 2, 'QS - Q = (sat. Q at S) - Q 0.01 g/kg'],
|
|
48
|
+
'G' : [6, 3, 'FW = (QS - Q)W = (evap. param.) 0.1 g/kg m/s'],
|
|
49
|
+
'I' : [7, 0, 'UA (sensible-heat--transport 0.1 @C m/s'],
|
|
50
|
+
'J' : [7, 1, 'VA parameters) 0.1 @C m/s'],
|
|
51
|
+
'K' : [7, 2, 'UQ (latent-heat--transport 0.1 g/kg m/s'],
|
|
52
|
+
'L' : [7, 3, 'VQ parameters) 0.1 g/kg m/s'],
|
|
53
|
+
'M' : [9, 0, 'FU 0.1 g/kg m/s'],
|
|
54
|
+
'N' : [9, 1, 'FV 0.1 g/kg m/s'],
|
|
55
|
+
'B1' : [8, 2, 'B = W**3 (high-resolution) 0.5 m**3/s**3'],
|
|
56
|
+
'B2' : [9, 3, 'B = W**3 (low-resolution) 5 m**3/s**3']
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
IDSID = 'd548001'
|
|
60
|
+
PVALS = {
|
|
61
|
+
'datadir' : PgLOG.PGLOG['DSDHOME'] + "/icoads/MSG3.0",
|
|
62
|
+
'docdir' : op.dirname(op.abspath(__file__)) + '/',
|
|
63
|
+
'readtxt' : "msg3.0_subset_readme.txt",
|
|
64
|
+
'readme' : "readme_msg3.0.",
|
|
65
|
+
'msgfile' : 'msg',
|
|
66
|
+
'statdoc' : 'R3.0-stat_doc.pdf',
|
|
67
|
+
'subset' : 'msgsubset',
|
|
68
|
+
'numstat' : 10,
|
|
69
|
+
'format' : "(i5,2i4,2f7.1,i5,10f8.2)",
|
|
70
|
+
'title' : " YEAR MON BSZ BLO BLA PID2 S1 S3 S5 M N S D HT X Y",
|
|
71
|
+
'varlist' : "",
|
|
72
|
+
'ptype' : "enh",
|
|
73
|
+
'resol' : 2,
|
|
74
|
+
'keys' : [],
|
|
75
|
+
'lats' : [],
|
|
76
|
+
'lons' : [],
|
|
77
|
+
'limits' : [],
|
|
78
|
+
'insize' : 0,
|
|
79
|
+
'outsize' : 0,
|
|
80
|
+
'ridx' : 0,
|
|
81
|
+
'rdir' : None
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
PGRQST = {}
|
|
85
|
+
INRECS = {}
|
|
86
|
+
OUTRECS = {}
|
|
87
|
+
|
|
88
|
+
#
|
|
89
|
+
# main function to run dsarch
|
|
90
|
+
#
|
|
91
|
+
def main():
|
|
92
|
+
|
|
93
|
+
global PGRQST
|
|
94
|
+
argv = sys.argv[1:]
|
|
95
|
+
PgLOG.PGLOG['LOGFILE'] = "icoads.log"
|
|
96
|
+
|
|
97
|
+
for arg in argv:
|
|
98
|
+
if arg == "-b":
|
|
99
|
+
PgLOG.PGLOG['BCKGRND'] = 1
|
|
100
|
+
elif arg == "-d":
|
|
101
|
+
PgLOG.PGLOG['DBGLEVEL'] = 1000
|
|
102
|
+
elif re.match(r'^-', arg):
|
|
103
|
+
PgLOG.pglog(arg + ": Unknown Option", PgLOG.LGEREX)
|
|
104
|
+
elif re.match(r'^(\d+)$', arg):
|
|
105
|
+
if PVALS['ridx']: PgLOG.pglog("{}: Request Index ({}) given already".format(arg, PVALS['ridx']), PgLOG.LGEREX)
|
|
106
|
+
PVALS['ridx'] = int(arg)
|
|
107
|
+
else:
|
|
108
|
+
if PVALS['rdir']: PgLOG.pglog("{}: Request Directory ({}) given already".format(arg, PVALS['rdir']), PgLOG.LGEREX)
|
|
109
|
+
PVALS['rdir'] = arg
|
|
110
|
+
|
|
111
|
+
PgLOG.cmdlog("msg3.0_subset {}".format(' '.join(argv)))
|
|
112
|
+
PGRQST = PgSubset.valid_subset_request(PVALS['ridx'], PVALS['rdir'], IDSID, PgLOG.LGWNEX)
|
|
113
|
+
if not PVALS['rdir']: PVALS['rdir'] = PgLOG.join_paths(PgLOG.PGLOG['RQSTHOME'], PGRQST['rqstid'])
|
|
114
|
+
process_subset_request()
|
|
115
|
+
PgLOG.cmdlog()
|
|
116
|
+
sys.exit(0)
|
|
117
|
+
|
|
118
|
+
#
|
|
119
|
+
# process a validated subset request
|
|
120
|
+
#
|
|
121
|
+
def process_subset_request():
|
|
122
|
+
|
|
123
|
+
if not op.isdir(PVALS['rdir']):
|
|
124
|
+
PgFile.make_local_directory(PVALS['rdir'], PgLOG.LGWNEX)
|
|
125
|
+
else:
|
|
126
|
+
if PgSubset.request_built(PVALS['ridx'], PVALS['rdir'], PVALS['msgfile'], PGRQST['fcount'], PgLOG.LGWNEX):
|
|
127
|
+
return PgLOG.pglog("MSG Subset Request built already for Index {}".format(PVALS['ridx']), PgLOG.LOGWRN)
|
|
128
|
+
PgSubset.clean_subset_request(PVALS['ridx'], PVALS['rdir'], None, PgLOG.LGWNEX)
|
|
129
|
+
|
|
130
|
+
cmdfiles = create_cmd_file()
|
|
131
|
+
process_data(cmdfiles)
|
|
132
|
+
|
|
133
|
+
PgFile.change_local_directory(PVALS['rdir'], PgLOG.LGWNEX)
|
|
134
|
+
offset = cnt = PVALS['outsize'] = 0
|
|
135
|
+
files = PgFile.local_glob("*{}*".format(PVALS['limits']))
|
|
136
|
+
for wfile in files:
|
|
137
|
+
PVALS['outsize'] += files[wfile]['data_size']
|
|
138
|
+
ms = re.search(r'(\d+)$', wfile)
|
|
139
|
+
if ms:
|
|
140
|
+
n = int(ms.group(1))
|
|
141
|
+
if n == 1: offset = cnt
|
|
142
|
+
order = offset + n
|
|
143
|
+
else:
|
|
144
|
+
order = cnt + 1
|
|
145
|
+
|
|
146
|
+
if PGRQST['file_format']: (wfile, fmt) = PgFile.compress_local_file(wfile, PGRQST['file_format'], 1)
|
|
147
|
+
PgSubset.add_subset_file(PVALS['ridx'], wfile, None, "D", "ASCII", order, None, PgLOG.LGWNEX)
|
|
148
|
+
cnt += 1
|
|
149
|
+
|
|
150
|
+
if cnt > 0:
|
|
151
|
+
wfile = write_readme()
|
|
152
|
+
cnt += 1
|
|
153
|
+
PgSubset.add_subset_file(PVALS['ridx'], wfile, None, "O", "TEXT", cnt, None, PgLOG.LGWNEX)
|
|
154
|
+
wfile = PVALS['statdoc']
|
|
155
|
+
cnt += 1
|
|
156
|
+
PgSubset.add_subset_file(PVALS['ridx'], wfile, PVALS['docdir'] + wfile, "O", "PDF", cnt, None, PgLOG.LGWNEX)
|
|
157
|
+
wfile = PVALS['msgfile']
|
|
158
|
+
cnt += 1
|
|
159
|
+
PgSubset.add_subset_file(PVALS['ridx'], wfile, PVALS['docdir'] + wfile, "O", "TEXT", cnt, None, PgLOG.LGWNEX)
|
|
160
|
+
if PVALS['insize'] > 0 and PVALS['insize'] != PGRQST['size_input']:
|
|
161
|
+
PgDBI.pgexec("UPDATE dsrqst SET size_input = {} WHERE rindex = {}".format(PVALS['insize'], PVALS['ridx']))
|
|
162
|
+
PgSubset.set_dsrqst_fcount(PVALS['ridx'], cnt, PVALS['insize'], PVALS['outsize'])
|
|
163
|
+
PgLOG.pglog("{} MSG subset files added to Request Index {}".format(cnt, PVALS['ridx']), PgLOG.LOGWRN)
|
|
164
|
+
else:
|
|
165
|
+
PgSubset.set_dsrqst_fcount(PVALS['ridx'], 0, PVALS['insize'], 0)
|
|
166
|
+
|
|
167
|
+
#
|
|
168
|
+
# process reqest and create the command file for data processing
|
|
169
|
+
#
|
|
170
|
+
def create_cmd_file():
|
|
171
|
+
|
|
172
|
+
datafiles = []
|
|
173
|
+
variables = []
|
|
174
|
+
cmdfiles = {}
|
|
175
|
+
ystr = yend = None
|
|
176
|
+
rinfo = PGRQST['rinfo'] if PGRQST['rinfo'] else PGRQST['note']
|
|
177
|
+
|
|
178
|
+
for line in rinfo.split("&"):
|
|
179
|
+
ms = re.search(r'(\w+)=(.+)', line)
|
|
180
|
+
if not ms: continue
|
|
181
|
+
token = ms.group(1)
|
|
182
|
+
pstring = ms.group(2)
|
|
183
|
+
if token == "dates": # Date Limits
|
|
184
|
+
dates = pstring
|
|
185
|
+
PVALS['limits'] = dates.replace(' ', '.')
|
|
186
|
+
ms = re.search(r'(\d\d\d\d)\d\d\s(\d\d\d\d)\d\d', dates)
|
|
187
|
+
if ms:
|
|
188
|
+
ystr = int(ms.group(1))
|
|
189
|
+
yend = int(ms.group(2))
|
|
190
|
+
elif token == 'lats':
|
|
191
|
+
PVALS['lats'] = pstring
|
|
192
|
+
elif token == 'lons':
|
|
193
|
+
PVALS['lons'] = pstring
|
|
194
|
+
elif token == 'resol':
|
|
195
|
+
ms = re.match(r'^(\d)DEG', pstring)
|
|
196
|
+
if ms: PVALS['resol'] = int(ms.group(1))
|
|
197
|
+
elif token == 'ptype':
|
|
198
|
+
PVALS['ptype'] = pstring.lower()
|
|
199
|
+
elif token == 'vars': # Variable Names
|
|
200
|
+
variables = pstring.split(', ')
|
|
201
|
+
|
|
202
|
+
if not variables: PgLOG.pglog("{}: No variable specified for subset Request".format(PVALS['ridx']), PgLOG.LGEREX)
|
|
203
|
+
if not ystr: PgLOG.pglog("{}: No Time limits specified for subset Request".format(PVALS['ridx']), PgLOG.LGEREX)
|
|
204
|
+
if PVALS['resol'] == 1 and ystr < 1960: PVALS['resol'] = 2
|
|
205
|
+
pos = PgSubset.get_latitudes(PVALS['lats'], PVALS['resol'])
|
|
206
|
+
PVALS['lats'] = "{:7.2f} {:7.2f}".format(pos[0], pos[1])
|
|
207
|
+
pos = PgSubset.get_longitudes(PVALS['lons'], PVALS['resol'])
|
|
208
|
+
PVALS['lons'] = "{:7.2f} {:7.2f}".format(pos[0], pos[1])
|
|
209
|
+
datadir = "{}/{}deg".format(PVALS['datadir'], PVALS['resol'])
|
|
210
|
+
|
|
211
|
+
# write out command file for input of Fortran code 'subset'
|
|
212
|
+
PVALS['insize'] = 0
|
|
213
|
+
for token in variables:
|
|
214
|
+
group = VARARRAY[token][0]
|
|
215
|
+
datafiles = get_data_files(group, PVALS['ptype'], ystr, yend, datadir)
|
|
216
|
+
if not datafiles: continue
|
|
217
|
+
cmdfile = "{}.MSG.{}".format(PGRQST['rqstid'], token)
|
|
218
|
+
OUT = open(cmdfile, 'w')
|
|
219
|
+
pstring = "Variable name : {} , description : {}, format{}\n".format(token, VARARRAY[token][2], PVALS['format'])
|
|
220
|
+
PVALS['varlist'] += pstring
|
|
221
|
+
OUT.write(pstring)
|
|
222
|
+
OUT.write(PVALS['title'] + "\n")
|
|
223
|
+
OUT.write("{} :Group number\n".format(group))
|
|
224
|
+
OUT.write("{} :nstat\n".format(PVALS['numstat']))
|
|
225
|
+
OUT.write("{} :stat index num.\n".format(STATNUM[VARARRAY[token][1]]))
|
|
226
|
+
OUT.write("{} :missing data index check\n".format(MISSIDX[VARARRAY[token][1]]))
|
|
227
|
+
OUT.write(PVALS['format'] + "\n")
|
|
228
|
+
OUT.write("{} {} {} :lat-lon SW corn. and time limits\n".format(PVALS['lats'], PVALS['lons'], dates))
|
|
229
|
+
OUT.write("{} :data resolution (1 or 2)\n".format(PVALS['resol']))
|
|
230
|
+
OUT.write("{} :data type (std or enh)\n".format(PVALS['ptype']))
|
|
231
|
+
OUT.write(token + " :variable name\n")
|
|
232
|
+
OUT.write(datadir + "/\n") # MSG data path
|
|
233
|
+
OUT.write(PVALS['rdir'] + "/\n") # output data path
|
|
234
|
+
OUT.write("{}\n".format(len(datafiles))) # number of MSG data files
|
|
235
|
+
for datafile in datafiles:
|
|
236
|
+
info = PgFile.check_local_file("{}/{}".format(datadir, datafile))
|
|
237
|
+
if info: PVALS['insize'] += info['data_size']
|
|
238
|
+
OUT.write(datafile + "\n")
|
|
239
|
+
|
|
240
|
+
OUT.close()
|
|
241
|
+
cmdfiles[token] = cmdfile
|
|
242
|
+
|
|
243
|
+
return cmdfiles
|
|
244
|
+
|
|
245
|
+
#
|
|
246
|
+
# gather data files
|
|
247
|
+
#
|
|
248
|
+
def get_data_files(group, ptype, ystr, yend, datadir):
|
|
249
|
+
|
|
250
|
+
datafiles = []
|
|
251
|
+
|
|
252
|
+
prefix = "{}g{}".format(ptype, group)
|
|
253
|
+
for yr in range(ystr, yend+1):
|
|
254
|
+
datafile = prefix + ".{}".format(yr)
|
|
255
|
+
if op.isfile("{}/{}".format(datadir, datafile)):
|
|
256
|
+
datafiles.append(datafile) # include existing files only
|
|
257
|
+
|
|
258
|
+
return datafiles
|
|
259
|
+
|
|
260
|
+
#
|
|
261
|
+
# process data and create the sub-dataset
|
|
262
|
+
#
|
|
263
|
+
def process_data(cmdfiles):
|
|
264
|
+
|
|
265
|
+
for var in cmdfiles:
|
|
266
|
+
infile = cmdfiles[var]
|
|
267
|
+
INRECS[var] = OUTRECS[var] = 0
|
|
268
|
+
if not op.isfile(infile): continue # no input file, skip it
|
|
269
|
+
retmsg = PgLOG.pgsystem("{} < {}".format(PVALS['subset'], infile), PgLOG.LGWNEX, 23)
|
|
270
|
+
if retmsg:
|
|
271
|
+
for line in retmsg.split("\n"):
|
|
272
|
+
ms = re.match(r'^\s*(IN|OUT)RECS:\s+(\d+)', line)
|
|
273
|
+
if ms:
|
|
274
|
+
if ms.group(1) == 'IN':
|
|
275
|
+
INRECS[var] = int(ms.group(2))
|
|
276
|
+
else:
|
|
277
|
+
OUTRECS[var] = int(ms.group(2))
|
|
278
|
+
if PgLOG.PGLOG['DBGLEVEL']: PgLOG.pgdbg(1000, line)
|
|
279
|
+
|
|
280
|
+
PgLOG.pgsystem("rm -f " + infile)
|
|
281
|
+
|
|
282
|
+
#
|
|
283
|
+
# create a readme file specified to the request
|
|
284
|
+
#
|
|
285
|
+
def write_readme():
|
|
286
|
+
|
|
287
|
+
user = PgDBI.get_ruser_names(PGRQST['email'])
|
|
288
|
+
readme = PVALS['readme'] + PGRQST['rqstid'].lower()
|
|
289
|
+
PgLOG.pglog("Create Readme file " + readme, PgLOG.LOGWRN)
|
|
290
|
+
URM = open(readme, 'w')
|
|
291
|
+
RTXT = open(PVALS['docdir'] + PVALS['readtxt'], 'r')
|
|
292
|
+
line = RTXT.readline()
|
|
293
|
+
while line:
|
|
294
|
+
if re.match(r'^#', line): # skip comment line
|
|
295
|
+
line = RTXT.readline()
|
|
296
|
+
continue
|
|
297
|
+
if re.search(r'__VARLIST__', line): # print user-selected variable list
|
|
298
|
+
URM.write(PVALS['varlist'])
|
|
299
|
+
line = RTXT.readline()
|
|
300
|
+
continue
|
|
301
|
+
|
|
302
|
+
if re.search(r'__USER__', line):
|
|
303
|
+
line = line.replace('__USER__', "{}<{}>".format(user['name'], PGRQST['email']))
|
|
304
|
+
elif re.search(r'__LATS__', line):
|
|
305
|
+
line = line.replace('__LATS__', PVALS['lats'])
|
|
306
|
+
elif re.search(r'__LONS__', line):
|
|
307
|
+
line = line.replace('__LONS__', PVALS['lons'])
|
|
308
|
+
elif re.search(r'__DATES__', line):
|
|
309
|
+
line = line.replace('__DATES__', PVALS['limits'])
|
|
310
|
+
elif re.search(r'__RESOL__', line):
|
|
311
|
+
line = line.replace('__RESOL__', str(PVALS['resol']))
|
|
312
|
+
elif re.search(r'__PTYPE__', line):
|
|
313
|
+
line = line.replace('__PTYPE__', PVALS['ptype'])
|
|
314
|
+
elif re.search(r'__INRECS__', line):
|
|
315
|
+
line = line.replace('__INRECS__', get_recs_string(INRECS))
|
|
316
|
+
elif re.search(r'__OUTRECS__', line):
|
|
317
|
+
line = line.replace('__OUTRECS__', get_recs_string(OUTRECS))
|
|
318
|
+
elif re.search(r'__INSIZES__', line):
|
|
319
|
+
line = line.replace('__INSIZES__', "{:.3f} MB".format(PVALS['insize']/1000000.))
|
|
320
|
+
elif re.search(r'__OUTSIZES__', line):
|
|
321
|
+
line = line.replace('__OUTSIZES__', "{:.3f} MB".format(PVALS['outsize']/1000000.))
|
|
322
|
+
URM.write(line)
|
|
323
|
+
line = RTXT.readline()
|
|
324
|
+
|
|
325
|
+
RTXT.close()
|
|
326
|
+
URM.close()
|
|
327
|
+
|
|
328
|
+
return readme
|
|
329
|
+
|
|
330
|
+
#
|
|
331
|
+
# get string buffer of in/out record counts
|
|
332
|
+
#
|
|
333
|
+
def get_recs_string(recs):
|
|
334
|
+
|
|
335
|
+
str = ''
|
|
336
|
+
for var in recs:
|
|
337
|
+
if str: str += ", "
|
|
338
|
+
str += "{}({})".format(recs[var], var)
|
|
339
|
+
|
|
340
|
+
return str
|
|
341
|
+
|
|
342
|
+
#
|
|
343
|
+
# call main() to start program
|
|
344
|
+
#
|
|
345
|
+
if __name__ == "__main__": main()
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
#
|
|
3
|
+
##################################################################################
|
|
4
|
+
#
|
|
5
|
+
# Title : msg_download
|
|
6
|
+
# Author : Zaihua Ji, zji@ucar.edu
|
|
7
|
+
# Date : 03/02/2021
|
|
8
|
+
# 2025-03-03 transferred to package rda_python_icoads from
|
|
9
|
+
# https://github.com/NCAR/rda-icoads.git
|
|
10
|
+
# Purpose : download MSG monthly update tar file and untar it for dsupdt
|
|
11
|
+
#
|
|
12
|
+
# Github : https://github.com/NCAR/rda-python-icoads.git
|
|
13
|
+
#
|
|
14
|
+
##################################################################################
|
|
15
|
+
|
|
16
|
+
import sys
|
|
17
|
+
import re
|
|
18
|
+
from os import path as op
|
|
19
|
+
from rda_python_common import PgLOG
|
|
20
|
+
from rda_python_common import PgUtil
|
|
21
|
+
from rda_python_common import PgFile
|
|
22
|
+
|
|
23
|
+
LFILES = [
|
|
24
|
+
"MSG1_R3.0.2_ENH_",
|
|
25
|
+
"MSG1_R3.0.2_STD_",
|
|
26
|
+
"MSG1_R3.0.2_ENH_EQ_",
|
|
27
|
+
"MSG1_R3.0.2_STD_EQ_",
|
|
28
|
+
"MSG2_R3.0.2_ENH_",
|
|
29
|
+
"MSG2_R3.0.2_STD_",
|
|
30
|
+
]
|
|
31
|
+
MFILES = [
|
|
32
|
+
["MSG1_", ".?.ENH.gz"],
|
|
33
|
+
["MSG1_", ".?.STD.gz"],
|
|
34
|
+
["MSG1_", ".?.ENH.S.gz"],
|
|
35
|
+
["MSG1_", ".?.STD.S.gz"],
|
|
36
|
+
["MSG2_", ".?.ENH.gz"],
|
|
37
|
+
["MSG2_", ".?.STD.gz"],
|
|
38
|
+
]
|
|
39
|
+
LCNT = 6
|
|
40
|
+
|
|
41
|
+
OPTIONS = {
|
|
42
|
+
'CD' : None,
|
|
43
|
+
'ED' : None,
|
|
44
|
+
'NL' : 0,
|
|
45
|
+
'NS' : 0,
|
|
46
|
+
'MU' : 0
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
WEBURL = "https://www.ncei.noaa.gov/data/international-comprehensive-ocean-atmosphere/v3/archive/msg/"
|
|
50
|
+
SUBDIR = PgLOG.PGLOG['DSDHOME'] + "/icoads/MSG3.0"
|
|
51
|
+
#WPATH = "data/msg3.0.0"
|
|
52
|
+
#WPATH = "data/netcdf3.0.2"
|
|
53
|
+
WPATH = "data/netcdf3.0.2new"
|
|
54
|
+
PPATH = "../.."
|
|
55
|
+
WORKDIR = PgLOG.get_environment("ICOADSDIR", PgLOG.PGLOG['UPDTWKP'] + "/zji/icoads") + "/icoads_rt"
|
|
56
|
+
#SFMT = "ICOADS_v3.0.0_MSG-binary_d{}{}_c"
|
|
57
|
+
SFMT = "icoads-nrt_r3.0.2_msg-binary_d{}{}_c"
|
|
58
|
+
|
|
59
|
+
#
|
|
60
|
+
# main function to excecute this script
|
|
61
|
+
#
|
|
62
|
+
def main():
|
|
63
|
+
|
|
64
|
+
PgLOG.PGLOG['LOGFILE'] = "icoads.log"
|
|
65
|
+
argv = sys.argv[1:]
|
|
66
|
+
options = '|'.join(OPTIONS)
|
|
67
|
+
option = None
|
|
68
|
+
|
|
69
|
+
for arg in argv:
|
|
70
|
+
if arg == "-b":
|
|
71
|
+
PgLOG.PGLOG['BCKGRND'] = 1
|
|
72
|
+
option = None
|
|
73
|
+
continue
|
|
74
|
+
ms = re.match(r'^-({})$'.format(options), arg, re.I)
|
|
75
|
+
if ms:
|
|
76
|
+
option = ms.group(1).upper()
|
|
77
|
+
if re.match(r'^(NS|NL|MU)$', option):
|
|
78
|
+
OPTIONS[option] = 1
|
|
79
|
+
option = None
|
|
80
|
+
continue
|
|
81
|
+
elif re.match(r'^-.*', arg):
|
|
82
|
+
PgLOG.pglog(arg + ": Unknown Option", PgLOG.LGEREX)
|
|
83
|
+
elif option:
|
|
84
|
+
OPTIONS[option] = arg
|
|
85
|
+
option = None
|
|
86
|
+
elif not OPTIONS['ED']:
|
|
87
|
+
OPTIONS['ED'] = arg
|
|
88
|
+
else:
|
|
89
|
+
PgLOG.pglog(arg + ": Value passed in without leading option", PgLOG.LGEREX)
|
|
90
|
+
|
|
91
|
+
if not OPTIONS['ED']:
|
|
92
|
+
print("Usage: msg_download [-MU] [-NL] [-NS] [-CD CurrentDate] [-ED] EndDate")
|
|
93
|
+
print(" Provide end date for monthly MSG data to download it from $WEBURL")
|
|
94
|
+
print(" Option -MU - download/build files for multiple month")
|
|
95
|
+
print(" Option -NL - do not build local files")
|
|
96
|
+
print(" Option -NS - do not save subset files")
|
|
97
|
+
print(" Option -CD - optional current date, default to today")
|
|
98
|
+
print(" Option -ED - mandatory for the end data date")
|
|
99
|
+
sys.exit(0)
|
|
100
|
+
|
|
101
|
+
PgLOG.cmdlog("msg_download {}".format(' '.join(argv)))
|
|
102
|
+
PgFile.change_local_directory(WORKDIR)
|
|
103
|
+
|
|
104
|
+
if not OPTIONS['CD']: OPTIONS['CD'] = PgUtil.curdate()
|
|
105
|
+
|
|
106
|
+
diff = PgUtil.diffdate(OPTIONS['ED'], OPTIONS['CD'])
|
|
107
|
+
if diff > 0: PgLOG.pglog("{}: data date is later than current date {}".format(OPTIONS['ED'], OPTIONS['CD']), PgLOG.LGEREX)
|
|
108
|
+
|
|
109
|
+
while diff < 0:
|
|
110
|
+
process_msg_files()
|
|
111
|
+
if not OPTIONS['MU']: break
|
|
112
|
+
OPTIONS['ED'] = PgUtil.adddate(OPTIONS['ED'], 0, 1, 0)
|
|
113
|
+
diff = PgUtil.diffdate(OPTIONS['ED'], OPTIONS['CD'])
|
|
114
|
+
|
|
115
|
+
PgLOG.cmdlog()
|
|
116
|
+
sys.exit(0)
|
|
117
|
+
|
|
118
|
+
#
|
|
119
|
+
# process download and build MSG files for a month
|
|
120
|
+
#
|
|
121
|
+
def process_msg_files():
|
|
122
|
+
|
|
123
|
+
ms = re.match(r'^(\d+)-(\d+)', OPTIONS['ED'])
|
|
124
|
+
if ms:
|
|
125
|
+
yr = ms.group(1)
|
|
126
|
+
mn = ms.group(2)
|
|
127
|
+
if len(mn) == 1: mn = '{:02}'.format(int(mn))
|
|
128
|
+
else:
|
|
129
|
+
PgLOG.pglog(OPTIONS['ED'] + ": invalid date format", PgLOG.LGEREX)
|
|
130
|
+
|
|
131
|
+
sfile = SFMT.format(yr, mn)
|
|
132
|
+
cnt = 0
|
|
133
|
+
lfiles = [None]*LCNT
|
|
134
|
+
lexists = [0]*LCNT
|
|
135
|
+
for i in range(LCNT):
|
|
136
|
+
lfiles[i] = "{}{}-{}.tar".format(LFILES[i], yr, mn)
|
|
137
|
+
if PgFile.local_file_size(lfiles[i], 3) > 0:
|
|
138
|
+
lexists[i] = 1
|
|
139
|
+
cnt += 1
|
|
140
|
+
|
|
141
|
+
if cnt == LCNT:
|
|
142
|
+
PgLOG.pglog("{}-{}:: all {} local files created already for the month".format(yr, mn, cnt), PgLOG.LOGWRN)
|
|
143
|
+
return cnt
|
|
144
|
+
|
|
145
|
+
rfile = download_msg_file(sfile)
|
|
146
|
+
if not rfile: return PgLOG.pglog(sfile + ": remote file NOT exists", PgLOG.LOGWRN)
|
|
147
|
+
|
|
148
|
+
# untar downloaded remote file
|
|
149
|
+
# PgLOG.pgsystem("tar -xvf {} -C {}".format(rfile, WPATH), PgLOG.LOGWRN, 5)
|
|
150
|
+
PgLOG.pgsystem("tar -xvf " + rfile, PgLOG.LOGWRN, 5)
|
|
151
|
+
|
|
152
|
+
PgFile.change_local_directory(WPATH)
|
|
153
|
+
if not OPTIONS['NL']: build_msg_files(yr, mn, lfiles, lexists)
|
|
154
|
+
if not OPTIONS['NS']: build_subset_files(yr, mn)
|
|
155
|
+
PgLOG.pgsystem("rm -f MSG?_{}.{}*".format(yr, mn), PgLOG.LOGWRN, 1029)
|
|
156
|
+
PgFile.change_local_directory(PPATH)
|
|
157
|
+
|
|
158
|
+
#
|
|
159
|
+
# download a MSG tar file from remote web server
|
|
160
|
+
#
|
|
161
|
+
def download_msg_file(sfile):
|
|
162
|
+
|
|
163
|
+
rfile = sfile + ".tar"
|
|
164
|
+
cmd = "pgwget -ul {} -rn {} -ex tar -fn {} -cr".format(WEBURL, sfile, rfile)
|
|
165
|
+
PgLOG.pgsystem(cmd, PgLOG.LOGWRN, 5)
|
|
166
|
+
if PgFile.check_local_file(rfile): return rfile
|
|
167
|
+
|
|
168
|
+
return None
|
|
169
|
+
|
|
170
|
+
#
|
|
171
|
+
# build up the local MSG files for archive
|
|
172
|
+
#
|
|
173
|
+
def build_msg_files(yr, mn, lfiles, lexists):
|
|
174
|
+
|
|
175
|
+
for i in range(LCNT):
|
|
176
|
+
if lexists[i]: continue
|
|
177
|
+
lfile = "{}/{}".format(PPATH, lfiles[i])
|
|
178
|
+
mfiles = "{}{}.{}{}".format(MFILES[i][0], yr, mn, MFILES[i][1])
|
|
179
|
+
PgLOG.pgsystem("tar -cvf {} {}".format(lfile, mfiles), PgLOG.LOGWRN, 1029)
|
|
180
|
+
|
|
181
|
+
#
|
|
182
|
+
# build up the subset MSG files
|
|
183
|
+
#
|
|
184
|
+
def build_subset_files(yr, mn):
|
|
185
|
+
|
|
186
|
+
grps = [3,4,5,6,7,9]
|
|
187
|
+
omonth = False if mn == '01' else True
|
|
188
|
+
for grp in grps:
|
|
189
|
+
PgLOG.pgsystem("gunzip MSG?_{}.{}.{}.???.gz".format(yr, mn, grp), PgLOG.LOGWRN, 1029)
|
|
190
|
+
build_one_subset_file("MSG1_{}.{}.{}.ENH".format(yr, mn, grp), 'enh', grp, 1, yr, omonth)
|
|
191
|
+
build_one_subset_file("MSG1_{}.{}.{}.STD".format(yr, mn, grp), 'std', grp, 1, yr, omonth)
|
|
192
|
+
build_one_subset_file("MSG2_{}.{}.{}.ENH".format(yr, mn, grp), 'enh', grp, 2, yr, omonth)
|
|
193
|
+
build_one_subset_file("MSG2_{}.{}.{}.STD".format(yr, mn, grp), 'std', grp, 2, yr, omonth)
|
|
194
|
+
|
|
195
|
+
#
|
|
196
|
+
# build one subset MSG file
|
|
197
|
+
#
|
|
198
|
+
def build_one_subset_file(nfile, type, grp, deg, yr, omonth):
|
|
199
|
+
|
|
200
|
+
# check and get the existing tar file
|
|
201
|
+
lfile = "{}g{}.{}".format(type, grp, yr)
|
|
202
|
+
tfile = "{}/{}deg/{}".format(SUBDIR, deg, lfile)
|
|
203
|
+
|
|
204
|
+
if omonth and not op.isfile(lfile) and op.isfile(tfile): PgLOG.pgsystem("cp -f {} {}".format(tfile, lfile), PgLOG.LGWNEX)
|
|
205
|
+
PgLOG.pgsystem("cat {} >> {}".format(nfile, lfile), PgLOG.LGWNEX)
|
|
206
|
+
PgLOG.pgsystem("cp -f {} {}".format(lfile, tfile), PgLOG.LGWNEX)
|
|
207
|
+
|
|
208
|
+
#
|
|
209
|
+
# call main() to start program
|
|
210
|
+
#
|
|
211
|
+
if __name__ == "__main__": main()
|