1
|
|
|
# -*- coding: utf-8 -*- |
2
|
|
|
# vim:fileencoding=utf-8 |
3
|
|
|
# |
4
|
|
|
# Copyright (c) 2015-2018 Stefan Bender |
5
|
|
|
# |
6
|
|
|
# This file is part of sciapy. |
7
|
|
|
# sciapy is free software: you can redistribute it or modify it |
8
|
|
|
# under the terms of the GNU General Public License as published by |
9
|
|
|
# the Free Software Foundation, version 2. |
10
|
|
|
# See accompanying LICENSE file or http://www.gnu.org/licenses/gpl-2.0.html. |
11
|
1 |
|
"""SCIAMACHY level 2 number density retrieval results interface |
12
|
|
|
|
13
|
|
|
Interface classes for the level 2 retrieval results from text (ascii) |
14
|
|
|
files and netcdf files for further processing. |
15
|
|
|
""" |
16
|
|
|
|
17
|
1 |
|
from __future__ import absolute_import, division, print_function |
18
|
|
|
|
19
|
1 |
|
import os |
20
|
1 |
|
import sys |
21
|
1 |
|
import datetime as dt |
22
|
|
|
|
23
|
1 |
|
import numpy as np |
24
|
1 |
|
try: |
25
|
1 |
|
from netCDF4 import Dataset as netcdf_file |
26
|
1 |
|
fmtargs = {"format": "NETCDF4"} |
27
|
|
|
except ImportError: |
28
|
|
|
try: |
29
|
|
|
from scipy.io.netcdf import netcdf_file |
30
|
|
|
fmtargs = {"version": 1} |
31
|
|
|
except ImportError: |
32
|
|
|
from pupynere import netcdf_file |
33
|
|
|
fmtargs = {"version": 1} |
34
|
|
|
|
35
|
1 |
|
__all__ = ["scia_density", "scia_densities"] |
36
|
|
|
|
37
|
1 |
|
try: |
38
|
1 |
|
_UTC = dt.timezone.utc |
39
|
|
|
except AttributeError: |
40
|
|
|
# python 2.7 |
41
|
|
|
class UTC(dt.tzinfo): |
|
|
|
|
42
|
|
|
def utcoffset(self, d): |
43
|
|
|
return dt.timedelta(0) |
44
|
|
|
def tzname(self, d): |
45
|
|
|
return "UTC" |
46
|
|
|
def dst(self, d): |
47
|
|
|
return dt.timedelta(0) |
48
|
|
|
_UTC = UTC() |
49
|
|
|
|
50
|
1 |
|
_meas_dtypes = [[('gp_id', int), |
51
|
|
|
('alt_max', float), ('alt', float), ('alt_min', float), |
52
|
|
|
('lat_max', float), ('lat', float), ('lat_min', float), |
53
|
|
|
('density', float), ('dens_err_meas', float), |
54
|
|
|
('dens_err_tot', float), ('dens_tot', float)], |
55
|
|
|
[('gp_id', int), |
56
|
|
|
('alt_max', float), ('alt', float), ('alt_min', float), |
57
|
|
|
('lat_max', float), ('lat', float), ('lat_min', float), |
58
|
|
|
('longitude', float), |
59
|
|
|
('density', float), ('dens_err_meas', float), |
60
|
|
|
('dens_err_tot', float), ('dens_tot', float)], |
61
|
|
|
[('gp_id', int), |
62
|
|
|
('alt_max', float), ('alt', float), ('alt_min', float), |
63
|
|
|
('lat_max', float), ('lat', float), ('lat_min', float), |
64
|
|
|
('longitude', float), |
65
|
|
|
('density', float), ('dens_err_meas', float), |
66
|
|
|
('dens_err_tot', float), ('dens_tot', float), |
67
|
|
|
('apriori', float)], |
68
|
|
|
[('gp_id', int), |
69
|
|
|
('alt_max', float), ('alt', float), ('alt_min', float), |
70
|
|
|
('lat_max', float), ('lat', float), ('lat_min', float), |
71
|
|
|
('longitude', float), |
72
|
|
|
('density', float), ('dens_err_meas', float), |
73
|
|
|
('dens_err_tot', float), ('dens_tot', float), |
74
|
|
|
('apriori', float), ('akdiag', float)]] |
75
|
|
|
|
76
|
|
|
|
77
|
1 |
|
def _unique_values(vals): |
78
|
1 |
|
ldum = [] |
79
|
1 |
|
[ldum.append(i) for i in vals if not ldum.count(i)] |
80
|
1 |
|
return np.asarray(ldum).flatten() |
81
|
|
|
|
82
|
|
|
|
83
|
1 |
View Code Duplication |
class scia_density(object): |
|
|
|
|
84
|
|
|
"""SCIAMACHY single scan retrieved number densities""" |
85
|
1 |
|
def __init__(self): |
86
|
|
|
self.gp_id = 0 |
87
|
|
|
self.alt_min = 0. |
88
|
|
|
self.alt = 0. |
89
|
|
|
self.alt_max = 0. |
90
|
|
|
self.lat_min = 0. |
91
|
|
|
self.lat = 0. |
92
|
|
|
self.lat_max = 0. |
93
|
|
|
self.lon = 0. |
94
|
|
|
self.density = 0. |
95
|
|
|
self.dens_err_meas = 0. |
96
|
|
|
self.dens_err_tot = 0. |
97
|
|
|
self.dens_tot = 0. |
98
|
|
|
self.akdiag = 0. |
99
|
|
|
self.apriori = 0. |
100
|
|
|
|
101
|
|
|
|
102
|
1 |
View Code Duplication |
class scia_densities(object): |
|
|
|
|
103
|
|
|
"""SCIAMACHY orbital retrieved number densities |
104
|
|
|
|
105
|
|
|
Class interface to orbit-wise SCIAMACHY retrieval results. |
106
|
|
|
The attributes are based on the text file layout and are |
107
|
|
|
tied to the NO retrieval for now. |
108
|
|
|
|
109
|
|
|
Parameters |
110
|
|
|
---------- |
111
|
|
|
ref_date: str, optional |
112
|
|
|
The reference date on which to base the date calculations on. |
113
|
|
|
Default: "2000-01-01" |
114
|
|
|
ver: str, optional |
115
|
|
|
Explicit density version, used for exporting the data. |
116
|
|
|
Not used if set to `None`. |
117
|
|
|
Default: `None` |
118
|
|
|
data_ver: str, optional |
119
|
|
|
Level 2 data version to use, as "ver" used for exporting. |
120
|
|
|
Not used if set to `None`. |
121
|
|
|
Default: `None` |
122
|
|
|
|
123
|
|
|
Attributes |
124
|
|
|
---------- |
125
|
|
|
version |
126
|
|
|
file version string |
127
|
|
|
data_version |
128
|
|
|
level 2 data version |
129
|
|
|
date0 |
130
|
|
|
reference date |
131
|
|
|
nalt |
132
|
|
|
number of altitudes in the orbit |
133
|
|
|
nlat |
134
|
|
|
number of latitudes in the orbit |
135
|
|
|
nlon |
136
|
|
|
number of longitudes in the orbit, if longitudes are available |
137
|
|
|
orbit |
138
|
|
|
SCIAMACHY/Envisat orbit number |
139
|
|
|
date |
140
|
|
|
number of days of the orbit counting from the reference date |
141
|
|
|
date0 |
142
|
|
|
alts_min |
143
|
|
|
alts |
144
|
|
|
alts_max |
145
|
|
|
the altitude bins: minimum, central, and maximum altitude |
146
|
|
|
lats_min |
147
|
|
|
lats |
148
|
|
|
lats_max |
149
|
|
|
the latitude bins: minimum, central, and maximum latitude |
150
|
|
|
lons: |
151
|
|
|
the central longitude of the bins, only used if available |
152
|
|
|
|
153
|
|
|
densities |
154
|
|
|
NO number densities in the bins, (nlat, nalt) array_like |
155
|
|
|
dens_err_meas |
156
|
|
|
NO number densities measurement uncertainty, |
157
|
|
|
(nlat, nalt) array_like |
158
|
|
|
dens_err_tot |
159
|
|
|
NO number densities total uncertainty, (nlat, nalt) array_like |
160
|
|
|
dens_tot |
161
|
|
|
total number densities calculated and interpolated NRLMSIS-00 |
162
|
|
|
values, (nlat, nalt) array_like |
163
|
|
|
|
164
|
|
|
apriori |
165
|
|
|
prior NO number densities, (nlat, nalt) array_like if available, |
166
|
|
|
otherwise `None` |
167
|
|
|
akdiag |
168
|
|
|
diagonal element of the averaging kernel matrix at the retrieval |
169
|
|
|
grid point. (nlat, nalt) array_like if available otherwise `None` |
170
|
|
|
|
171
|
|
|
Methods |
172
|
|
|
------- |
173
|
|
|
read_from_textfile |
174
|
|
|
read_from_netcdf |
175
|
|
|
read_from_file |
176
|
|
|
write_to_textfile |
177
|
|
|
write_to_netcdf |
178
|
|
|
|
179
|
|
|
Note |
180
|
|
|
---- |
181
|
|
|
The variables are empty when initialized, use one of the |
182
|
|
|
read_from_...() methods to fill with actual data. |
183
|
|
|
""" |
184
|
1 |
|
def __init__(self, ref_date="2000-01-01", ver=None, data_ver=None): |
185
|
1 |
|
self.version = ver |
186
|
1 |
|
self.data_version = data_ver |
187
|
1 |
|
self.date0 = dt.datetime.strptime(ref_date, "%Y-%m-%d").replace(tzinfo=_UTC) |
188
|
1 |
|
self.nalt = 0 |
189
|
1 |
|
self.nlat = 0 |
190
|
1 |
|
self.nlon = 0 |
191
|
1 |
|
self.orbit = -1 |
192
|
1 |
|
self.date = -1 |
193
|
1 |
|
self.alts_min = np.array([]) |
194
|
1 |
|
self.alts = np.array([]) |
195
|
1 |
|
self.alts_max = np.array([]) |
196
|
1 |
|
self.lats_min = np.array([]) |
197
|
1 |
|
self.lats = np.array([]) |
198
|
1 |
|
self.lats_max = np.array([]) |
199
|
1 |
|
self.lons = np.array([]) |
200
|
1 |
|
self.akdiag = None |
201
|
1 |
|
self.apriori = None |
202
|
|
|
|
203
|
1 |
|
def read_from_textfile(self, filename): |
204
|
|
|
"""Read NO densities from ascii table file |
205
|
|
|
|
206
|
|
|
Parameters |
207
|
|
|
---------- |
208
|
|
|
filename: str, file object or io.TextIOBase.buffer |
209
|
|
|
The filename or stream to read the data from. For example |
210
|
|
|
to read from stdin in python 3, pass `sys.stdin.buffer`. |
211
|
|
|
""" |
212
|
1 |
|
if hasattr(filename, 'seek'): |
213
|
|
|
f = filename |
214
|
|
|
else: |
215
|
1 |
|
f = open(filename, 'rb') |
216
|
|
|
# example filename:000NO_orbit_41467_20100203_Dichten.txt |
217
|
1 |
|
fn_fields = os.path.basename(filename).split('_') |
218
|
1 |
|
self.orbit = int(fn_fields[2]) |
219
|
1 |
|
self.date = (dt.datetime.strptime(fn_fields[3], "%Y%m%d") |
220
|
|
|
.replace(tzinfo=_UTC) - self.date0).days |
221
|
1 |
|
if self.data_version is None: |
222
|
|
|
# try some heuristics to find the level 2 data version |
223
|
1 |
|
self.data_version = os.path.dirname(filename).split('v')[-1] |
224
|
1 |
|
line = f.readline() |
225
|
1 |
|
data = line.split() |
226
|
1 |
|
mydtype = _meas_dtypes[len(data) - 13] |
227
|
1 |
|
marr = np.genfromtxt(f, dtype=mydtype) |
228
|
1 |
|
f.close() |
229
|
|
|
|
230
|
|
|
# unique altitudes |
231
|
1 |
|
self.alts_min = _unique_values(marr['alt_min']) |
232
|
1 |
|
self.alts = _unique_values(marr['alt']) |
233
|
1 |
|
self.alts_max = _unique_values(marr['alt_max']) |
234
|
|
|
|
235
|
|
|
# unique latitudes |
236
|
1 |
|
self.lats_min = _unique_values(marr['lat_min']) |
237
|
1 |
|
self.lats = _unique_values(marr['lat']) |
238
|
1 |
|
self.lats_max = _unique_values(marr['lat_max']) |
239
|
|
|
|
240
|
|
|
# unique longitudes if available |
241
|
1 |
|
try: |
242
|
1 |
|
self.lons = _unique_values(marr['longitude']) |
243
|
|
|
except: |
244
|
|
|
pass |
245
|
|
|
|
246
|
1 |
|
self.nalt = len(self.alts) |
247
|
1 |
|
self.nlat = len(self.lats) |
248
|
1 |
|
self.nlon = len(self.lons) |
249
|
|
|
|
250
|
|
|
# reorder by latitude first, then altitude |
251
|
1 |
|
self.densities = marr['density'].flatten().reshape(self.nalt, self.nlat).transpose() |
252
|
1 |
|
self.dens_err_meas = marr['dens_err_meas'].flatten().reshape(self.nalt, self.nlat).transpose() |
253
|
1 |
|
self.dens_err_tot = marr['dens_err_tot'].flatten().reshape(self.nalt, self.nlat).transpose() |
254
|
1 |
|
self.dens_tot = marr['dens_tot'].flatten().reshape(self.nalt, self.nlat).transpose() |
255
|
|
|
|
256
|
|
|
# apriori if available |
257
|
1 |
|
try: |
258
|
1 |
|
self.apriori = marr['apriori'].flatten().reshape(self.nalt, self.nlat).transpose() |
259
|
|
|
except: |
260
|
|
|
pass |
261
|
|
|
# akdiag if available |
262
|
1 |
|
try: |
263
|
1 |
|
self.akdiag = marr['akdiag'].flatten().reshape(self.nalt, self.nlat).transpose() |
264
|
|
|
except: |
265
|
|
|
pass |
266
|
|
|
|
267
|
1 |
|
def write_to_textfile(self, filename): |
268
|
|
|
"""Write NO densities to ascii table files |
269
|
|
|
|
270
|
|
|
Parameters |
271
|
|
|
---------- |
272
|
|
|
filename: str or file object or io.TextIOBase.buffer |
273
|
|
|
The filename or stream to write the data to. For writing to |
274
|
|
|
stdout in python 3, pass `sys.stdout.buffer`. |
275
|
|
|
""" |
276
|
|
|
if hasattr(filename, 'seek'): |
277
|
|
|
f = filename |
278
|
|
|
else: |
279
|
|
|
f = open(filename, 'w') |
280
|
|
|
|
281
|
|
|
header = "%5s %13s %12s %13s %14s %12s %14s %12s %12s %12s %12s %12s" % ("GP_ID", |
282
|
|
|
"Max_Hoehe[km]", "Hoehe[km]", "Min_Hoehe[km]", |
283
|
|
|
"Max_Breite[°]", "Breite[°]", "Min_Breite[°]", |
284
|
|
|
"Laenge[°]", |
285
|
|
|
"Dichte[cm^-3]", "Fehler Mess[cm^-3]", |
286
|
|
|
"Fehler tot[cm^-3]", "Gesamtdichte[cm^-3]") |
287
|
|
|
if self.apriori is not None: |
288
|
|
|
header = header + " %12s" % ("apriori[cm^-3]",) |
289
|
|
|
if self.akdiag is not None: |
290
|
|
|
header = header + " %12s" % ("AKdiag",) |
291
|
|
|
print(header, file=f) |
292
|
|
|
|
293
|
|
|
oformat = "%5i %+1.5E %+1.5E %+1.5E %+1.5E %+1.5E %+1.5E %+1.5E %+1.5E %+1.5E %+1.5E %+1.5E" |
294
|
|
|
oformata = " %+1.5E" |
295
|
|
|
|
296
|
|
|
for i, a in enumerate(self.alts): |
297
|
|
|
for j, b in enumerate(self.lats): |
298
|
|
|
print(oformat % (i * self.nlat + j, |
299
|
|
|
self.alts_max[i], a, self.alts_min[i], |
300
|
|
|
self.lats_max[j], b, self.lats_min[j], self.lons[j], |
301
|
|
|
self.densities[j, i], self.dens_err_meas[j, i], |
302
|
|
|
self.dens_err_tot[j, i], self.dens_tot[j, i]), |
303
|
|
|
end="", file=f) |
304
|
|
|
if self.apriori is not None: |
305
|
|
|
print(" " + oformata % self.apriori[j, i], end="", file=f) |
306
|
|
|
if self.akdiag is not None: |
307
|
|
|
print(" " + oformata % self.akdiag[j, i], end="", file=f) |
308
|
|
|
print("", file=f) |
309
|
|
|
|
310
|
1 |
|
def write_to_netcdf(self, filename, close=True): |
311
|
|
|
"""Write NO densities to netcdf files |
312
|
|
|
|
313
|
|
|
This function has no stream, i.e. file object, support. |
314
|
|
|
Uses single precision floats (32 bit) to save space. |
315
|
|
|
|
316
|
|
|
Parameters |
317
|
|
|
---------- |
318
|
|
|
filename: str |
319
|
|
|
The name of the file to write the data to. |
320
|
|
|
close: bool, optional |
321
|
|
|
Whether or not to close the file after writing. |
322
|
|
|
Setting to `False` enables appending further data |
323
|
|
|
to the same file. |
324
|
|
|
Default: True |
325
|
|
|
|
326
|
|
|
Returns |
327
|
|
|
------- |
328
|
|
|
Nothing if `close` is True. If `close` is False, returns either an |
329
|
|
|
`netCDF4.Dataset`, |
330
|
|
|
`scipy.io.netcdf.netcdf_file` or |
331
|
|
|
`pupynere.netcdf_file` instance depending on availability. |
332
|
|
|
""" |
333
|
1 |
|
alts_min_out = np.asarray(self.alts_min).reshape(self.nalt) |
334
|
1 |
|
alts_out = np.asarray(self.alts).reshape(self.nalt) |
335
|
1 |
|
alts_max_out = np.asarray(self.alts_max).reshape(self.nalt) |
336
|
|
|
|
337
|
1 |
|
lats_min_out = np.asarray(self.lats_min).reshape(self.nlat) |
338
|
1 |
|
lats_out = np.asarray(self.lats).reshape(self.nlat) |
339
|
1 |
|
lats_max_out = np.asarray(self.lats_max).reshape(self.nlat) |
340
|
|
|
|
341
|
1 |
|
ncf = netcdf_file(filename, 'w', **fmtargs) |
342
|
|
|
|
343
|
1 |
|
if self.version is not None: |
344
|
1 |
|
ncf.version = self.version |
345
|
1 |
|
if self.data_version is not None: |
346
|
1 |
|
ncf.L2_data_version = self.data_version |
347
|
|
|
#ncf.creation_time = dt.datetime.utcnow().replace(tzinfo=_UTC).strftime("%a %b %d %Y %H:%M:%S %z (%Z)") |
348
|
1 |
|
ncf.creation_time = dt.datetime.utcnow().strftime("%a %b %d %Y %H:%M:%S +00:00 (UTC)") |
349
|
1 |
|
ncf.author = "Firstname Lastname" |
350
|
|
|
|
351
|
|
|
# create netcdf file |
352
|
1 |
|
ncf.createDimension('altitude', self.nalt) |
353
|
1 |
|
ncf.createDimension('latitude', self.nlat) |
354
|
1 |
|
ncf.createDimension('time', None) |
355
|
|
|
|
356
|
1 |
|
forbit = ncf.createVariable('orbit', np.dtype('int32').char, ('time',)) |
357
|
1 |
|
ftime = ncf.createVariable('time', np.dtype('int32').char, ('time',)) |
358
|
|
|
|
359
|
1 |
|
falts_min = ncf.createVariable('alt_min', np.dtype('float32').char, ('altitude',)) |
360
|
1 |
|
falts = ncf.createVariable('altitude', np.dtype('float32').char, ('altitude',)) |
361
|
1 |
|
falts_max = ncf.createVariable('alt_max', np.dtype('float32').char, ('altitude',)) |
362
|
1 |
|
flats_min = ncf.createVariable('lat_min', np.dtype('float32').char, ('latitude',)) |
363
|
1 |
|
flats = ncf.createVariable('latitude', np.dtype('float32').char, ('latitude',)) |
364
|
1 |
|
flats_max = ncf.createVariable('lat_max', np.dtype('float32').char, ('latitude',)) |
365
|
|
|
|
366
|
1 |
|
falts_min.units = 'km' |
367
|
1 |
|
falts_min.positive = 'up' |
368
|
1 |
|
falts.units = 'km' |
369
|
1 |
|
falts.positive = 'up' |
370
|
1 |
|
falts_max.units = 'km' |
371
|
1 |
|
falts_max.positive = 'up' |
372
|
1 |
|
flats_min.units = 'degrees_north' |
373
|
1 |
|
flats.units = 'degrees_north' |
374
|
1 |
|
flats_max.units = 'degrees_north' |
375
|
|
|
|
376
|
1 |
|
forbit.units = '1' |
377
|
1 |
|
forbit.long_name = 'SCIAMACHY/Envisat orbit number' |
378
|
1 |
|
ftime.units = 'days since {0}'.format(self.date0.isoformat(sep=' ')) |
379
|
1 |
|
ftime.standard_name = 'time' |
380
|
|
|
|
381
|
1 |
|
fdens = ncf.createVariable('density', np.dtype('float32').char, ('time', 'latitude', 'altitude')) |
382
|
1 |
|
fdens.units = 'cm^{-3}' |
383
|
1 |
|
fdens.standard_name = 'number_concentration_of_nitrogen_monoxide_molecules_in_air' |
384
|
1 |
|
fdens_err_meas = ncf.createVariable('error_meas', np.dtype('float32').char, ('time', 'latitude', 'altitude')) |
385
|
1 |
|
fdens_err_meas.units = 'cm^{-3}' |
386
|
1 |
|
fdens_err_meas.long_name = 'NO number density measurement error' |
387
|
1 |
|
fdens_err_tot = ncf.createVariable('error_tot', np.dtype('float32').char, ('time', 'latitude', 'altitude')) |
388
|
1 |
|
fdens_err_tot.units = 'cm^{-3}' |
389
|
1 |
|
fdens_err_tot.long_name = 'NO number density total error' |
390
|
1 |
|
fdens_tot = ncf.createVariable('density_air', np.dtype('float32').char, ('time', 'latitude', 'altitude')) |
391
|
1 |
|
fdens_tot.units = 'cm^{-3}' |
392
|
1 |
|
fdens_tot.long_name = 'approximate overall number concentration of air molecules (NRLMSIS-00)' |
393
|
|
|
|
394
|
1 |
|
ftime[:] = self.date |
395
|
1 |
|
forbit[:] = self.orbit |
396
|
|
|
|
397
|
1 |
|
falts_min[:] = alts_min_out |
398
|
1 |
|
falts[:] = alts_out |
399
|
1 |
|
falts_max[:] = alts_max_out |
400
|
1 |
|
flats_min[:] = lats_min_out |
401
|
1 |
|
flats[:] = lats_out |
402
|
1 |
|
flats_max[:] = lats_max_out |
403
|
|
|
# reorder by latitude first, then altitude |
404
|
1 |
|
fdens[0, :] = self.densities |
405
|
|
|
# reorder by latitude first, then altitude |
406
|
1 |
|
fdens_err_meas[0, :] = self.dens_err_meas |
407
|
1 |
|
fdens_err_tot[0, :] = self.dens_err_tot |
408
|
1 |
|
fdens_tot[0, :] = self.dens_tot |
409
|
|
|
|
410
|
|
|
# longitudes if they are available |
411
|
1 |
|
if self.nlon > 0: |
412
|
1 |
|
lons_out = np.asarray(self.lons).reshape(self.nlon) |
413
|
1 |
|
flons = ncf.createVariable('longitude', np.dtype('float32').char, ('time', 'latitude',)) |
414
|
1 |
|
flons.units = 'degrees_east' |
415
|
1 |
|
flons[0, :] = lons_out |
416
|
|
|
|
417
|
1 |
|
if self.apriori is not None: |
418
|
1 |
|
fapriori = ncf.createVariable('apriori', |
419
|
|
|
np.dtype('float32').char, ('time', 'latitude', 'altitude')) |
420
|
1 |
|
fapriori.units = 'cm^{-3}' |
421
|
1 |
|
fapriori.long_name = 'apriori NO number density' |
422
|
1 |
|
fapriori[0, :] = self.apriori |
423
|
|
|
|
424
|
1 |
|
if self.akdiag is not None: |
425
|
1 |
|
fakdiag = ncf.createVariable('akm_diagonal', |
426
|
|
|
np.dtype('float32').char, ('time', 'latitude', 'altitude')) |
427
|
1 |
|
fakdiag.units = '1' |
428
|
1 |
|
fakdiag.long_name = 'averaging kernel matrix diagonal element' |
429
|
1 |
|
fakdiag[0, :] = self.akdiag |
430
|
1 |
|
if close: |
431
|
|
|
ncf.close() |
432
|
|
|
else: |
433
|
1 |
|
return ncf |
434
|
|
|
|
435
|
1 |
|
def read_from_netcdf(self, filename, close=True): |
436
|
|
|
"""Read NO densities from netcdf files |
437
|
|
|
|
438
|
|
|
This function has no stream, i.e. file object support. |
439
|
|
|
|
440
|
|
|
Parameters |
441
|
|
|
---------- |
442
|
|
|
filename: str |
443
|
|
|
The filename to read the data from. |
444
|
|
|
close: bool, optional |
445
|
|
|
Whether or not to close the file after reading. |
446
|
|
|
Setting to `False` enables reading further data |
447
|
|
|
from the same file. |
448
|
|
|
Default: True |
449
|
|
|
|
450
|
|
|
Returns |
451
|
|
|
------- |
452
|
|
|
Nothing if `close` is True. If `close` is False, returns either an |
453
|
|
|
`netCDF4.Dataset`, |
454
|
|
|
`scipy.io.netcdf.netcdf_file` or |
455
|
|
|
`pupynere.netcdf_file` instance depending on availability. |
456
|
|
|
""" |
457
|
1 |
|
ncf = netcdf_file(filename, 'r') |
458
|
|
|
|
459
|
|
|
self.nalt = ncf.dimensions['altitude'] |
460
|
|
|
self.nlat = ncf.dimensions['latitude'] |
461
|
|
|
|
462
|
|
|
self.alts_min = ncf.variables['alt_min'][:] |
463
|
|
|
self.alts = ncf.variables['altitude'][:] |
464
|
|
|
self.alts_max = ncf.variables['alt_max'][:] |
465
|
|
|
self.lats_min = ncf.variables['lat_min'][:] |
466
|
|
|
self.lats = ncf.variables['latitude'][:] |
467
|
|
|
self.lats_max = ncf.variables['lat_max'][:] |
468
|
|
|
|
469
|
|
|
self.date = ncf.variable['time'][:] |
470
|
|
|
self.orbit = ncf.variable['orbit'][:] |
471
|
|
|
|
472
|
|
|
self.densities = ncf.variables['density'][:] |
473
|
|
|
self.dens_err_meas = ncf.variables['error_meas'][:] |
474
|
|
|
self.dens_err_tot = ncf.variables['error_tot'][:] |
475
|
|
|
self.dens_tot = ncf.variables['density_air'][:] |
476
|
|
|
|
477
|
|
|
# longitudes if they are available |
478
|
|
|
try: |
479
|
|
|
self.nlon = ncf.dimensions['longitude'] |
480
|
|
|
self.lons = ncf.variables['longitude'][:] |
481
|
|
|
except: |
482
|
|
|
pass |
483
|
|
|
|
484
|
|
|
# apriori |
485
|
|
|
try: |
486
|
|
|
self.apriori = ncf.variables['apriori'][:] |
487
|
|
|
except: |
488
|
|
|
pass |
489
|
|
|
|
490
|
|
|
# akm diagonal elements |
491
|
|
|
try: |
492
|
|
|
self.akdiag = ncf.variables['akm_diagonal'][:] |
493
|
|
|
except: |
494
|
|
|
pass |
495
|
|
|
|
496
|
|
|
if close: |
497
|
|
|
ncf.close() |
498
|
|
|
else: |
499
|
|
|
return ncf |
500
|
|
|
|
501
|
1 |
|
def read_from_file(self, filename): |
502
|
|
|
"""Wrapper to read NO desnities from files |
503
|
|
|
|
504
|
|
|
Simple wrapper to delegate reading the data from either netcdf |
505
|
|
|
or ascii files. Poor man's logic: simply try netcdf first, and |
506
|
|
|
if that fails, read as ascii. |
507
|
|
|
|
508
|
|
|
Parameters |
509
|
|
|
---------- |
510
|
|
|
filename: str |
511
|
|
|
The filename to read the data from. |
512
|
|
|
""" |
513
|
1 |
|
try: |
514
|
|
|
# try netcdf first |
515
|
1 |
|
self.read_from_netcdf(filename) |
516
|
1 |
|
except: |
517
|
|
|
# fall back to text file as a last resort |
518
|
1 |
|
self.read_from_textfile(filename) |
519
|
|
|
|
520
|
|
|
|
521
|
1 |
View Code Duplication |
def main(*args): |
|
|
|
|
522
|
|
|
argc = len(sys.argv) |
523
|
|
|
if argc < 2: |
524
|
|
|
print("Not enough arguments, Usage:\n" |
525
|
|
|
"{0} [input] output [< input]".format(sys.argv[0])) |
526
|
|
|
sys.exit(1) |
527
|
|
|
elif argc < 3: |
528
|
|
|
try: |
529
|
|
|
infile = sys.stdin.buffer # Python 3 |
530
|
|
|
except AttributeError: |
531
|
|
|
infile = sys.stdin |
532
|
|
|
outfile = sys.argv[1] |
533
|
|
|
else: |
534
|
|
|
infile = sys.argv[1] |
535
|
|
|
outfile = sys.argv[2] |
536
|
|
|
sdl = scia_densities() |
537
|
|
|
sdl.read_from_file(infile) |
|
|
|
|
538
|
|
|
sdl.write_to_netcdf(outfile) |
|
|
|
|
539
|
|
|
|
540
|
|
|
|
541
|
1 |
|
if __name__ == "__main__": |
542
|
|
|
sys.exit(main()) |
543
|
|
|
|