Passed
Push — master ( bdae2e...f0f30c )
by Stefan
03:19
created

sciapy.level2.density_pp.scia_density_day.append()   A

Complexity

Conditions 1

Size

Total Lines 29
Code Lines 22

Duplication

Lines 29
Ratio 100 %

Code Coverage

Tests 1
CRAP Score 1.8696

Importance

Changes 0
Metric Value
cc 1
eloc 22
nop 2
dl 29
loc 29
ccs 1
cts 22
cp 0.0455
crap 1.8696
rs 9.352
c 0
b 0
f 0
1
# -*- coding: utf-8 -*-
2
# vim:fileencoding=utf-8
3
#
4
# Copyright (c) 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 post-processed number densities interface
12
13
Interface classes for the level 2 post-processed retrieval results.
14
"""
15
16 1
from __future__ import absolute_import, division, print_function
17
18 1
import datetime as dt
19
20 1
import numpy as np
21 1
try:
22 1
	from netCDF4 import Dataset as netcdf_file
23 1
	fmtargs = {"format": "NETCDF4"}
24
except ImportError:
25
	try:
26
		from scipy.io.netcdf import netcdf_file
27
		fmtargs = {"version": 1}
28
	except ImportError:
29
		from pupynere import netcdf_file
30
		fmtargs = {"version": 1}
31
32 1
from .density import scia_densities
33
34 1
__all__ = ["scia_densities_pp", "scia_density_day"]
35
36
37 1 View Code Duplication
class scia_densities_pp(scia_densities):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
38
	"""Post-processed SCIAMACHY number densities
39
40
	Extends `scia_densities` with additional post-processing
41
	attributes such as (MSIS) temperature and density, local
42
	solar time, solar zenith angle, and geomagnetic latitudes
43
	and longitudes.
44
45
	This class only supports writing ascii files but reading to
46
	and writing from netcdf.
47
48
	Attributes
49
	----------
50
	temperature
51
		NRLMSISE-00 temperatures
52
	noem_no
53
		NOEM NO nuimber densities
54
	vmr
55
		NO vmr using the NRLMSISE-00 total air number densities
56
	lst
57
		Apparent local solar times
58
	mst
59
		Mean local solar times
60
	sza
61
		Solar zenith angles
62
	utchour
63
		UTC hours into measurement day
64
	utcdays
65
		Number of days since reference date
66
	gmlats
67
		IGRF-12 geomagentic latitudes
68
	gmlons
69
		IGRF-12 geomagentic longitudes
70
	aacgmgmlats
71
		AACGM geomagentic latitudes
72
	aacgmgmlons
73
		AACGM geomagentic longitudes
74
75
	Methods
76
	-------
77
	write_to_textfile
78
	write_to_netcdf
79
	read_from_netcdf
80
	to_xarray
81
	"""
82 1
	def __init__(self, ref_date="2000-01-01",
83
			ver=None, data_ver=None):
84 1
		self.filename = None
85 1
		self.temperature = None
86 1
		self.noem_no = None
87 1
		self.vmr = None
88 1
		self.lst = None
89 1
		self.mst = None
90 1
		self.sza = None
91 1
		self.utchour = None
92 1
		self.utcdays = None
93 1
		self.gmlats = None
94 1
		self.gmlons = None
95 1
		self.aacgmgmlats = None
96 1
		self.aacgmgmlons = None
97 1
		super(scia_densities_pp, self).__init__(
98
				ref_date=ref_date, ver=ver, data_ver=data_ver)
99
100 1
	def write_to_textfile(self, filename):
101
		"""Write the variables to ascii table files
102
103
		Parameters
104
		----------
105
		filename: str or file object or io.TextIOBase.buffer
106
			The filename or stream to write the data to. For writing to
107
			stdout in python 3, pass `sys.stdout.buffer`.
108
		"""
109
		if hasattr(filename, 'seek'):
110
			f = filename
111
		else:
112
			f = open(filename, 'w')
113
114
		header = "%5s %13s %12s %13s %14s  %12s %14s   %12s  %12s %12s %12s %12s" % ("GP_ID",
115
				"Max_Hoehe[km]", "Hoehe[km]", "Min_Hoehe[km]",
116
				"Max_Breite[°]", "Breite[°]", "Min_Breite[°]",
117
				"Laenge[°]",
118
				"Dichte[cm^-3]", "Fehler Mess[cm^-3]",
119
				"Fehler tot[cm^-3]", "Gesamtdichte[cm^-3]")
120
		if self.apriori is not None:
121
			header = header + " %12s" % ("apriori[cm^-3]",)
122
		if self.temperature is not None:
123
			header = header + " %12s" % ("T[K]",)
124
		if self.noem_no is not None:
125
			header = header + " %12s" % ("NOEM_NO[cm^-3]",)
126
		if self.akdiag is not None:
127
			header = header + " %12s" % ("AKdiag",)
128
		if self.lst is not None:
129
			header = header + " %12s" % ("LST",)
130
		if self.mst is not None:
131
			header = header + " %12s" % ("MST",)
132
		if self.sza is not None:
133
			header = header + " %12s" % ("SZA",)
134
		if self.utchour is not None:
135
			header = header + " %12s" % ("Hour",)
136
		if self.utcdays is not None:
137
			header = header + " %12s" % ("Days",)
138
		if self.gmlats is not None:
139
			header = header + " %12s" % ("GeomagLat",)
140
		if self.gmlons is not None:
141
			header = header + " %12s" % ("GeomagLon",)
142
		if self.aacgmgmlats is not None:
143
			header = header + " %12s" % ("AACGMGeomagLat",)
144
		if self.aacgmgmlons is not None:
145
			header = header + " %12s" % ("AACGMGeomagLon",)
146
		print(header, file=f)
147
148
		oformat = "%5i  %+1.5E %+1.5E  %+1.5E  %+1.5E %+1.5E  %+1.5E  %+1.5E   %+1.5E       %+1.5E      %+1.5E        %+1.5E"
149
		oformata = "  %+1.5E"
150
151
		for i, a in enumerate(self.alts):
152
			for j, b in enumerate(self.lats):
153
				print(oformat % (i * self.nlat + j,
154
					self.alts_max[i], a, self.alts_min[i],
155
					self.lats_max[j], b, self.lats_min[j], self.lons[j],
156
					self.densities[j, i], self.dens_err_meas[j, i],
157
					self.dens_err_tot[j, i], self.dens_tot[j, i]),
158
					end="", file=f)
159
				if self.apriori is not None:
160
					print(" " + oformata % self.apriori[j, i], end="", file=f)
161
				if self.temperature is not None:
162
					print(" " + oformata % self.temperature[j, i], end="", file=f)
163
				if self.noem_no is not None:
164
					print(" " + oformata % self.noem_no[j, i], end="", file=f)
165
				if self.akdiag is not None:
166
					print(" " + oformata % self.akdiag[j, i], end="", file=f)
167
				if self.lst is not None:
168
					print(" " + oformata % self.lst[j], end="", file=f)
169
				if self.mst is not None:
170
					print(" " + oformata % self.mst[j], end="", file=f)
171
				if self.sza is not None:
172
					print(" " + oformata % self.sza[j], end="", file=f)
173
				if self.utchour is not None:
174
					print(" " + oformata % self.utchour[j], end="", file=f)
175
				if self.utcdays is not None:
176
					print(" " + oformata % self.utcdays[j], end="", file=f)
177
				if self.gmlats is not None:
178
					print(" " + oformata % self.gmlats[j], end="", file=f)
179
				if self.gmlons is not None:
180
					print(" " + oformata % self.gmlons[j], end="", file=f)
181
				if self.aacgmgmlats is not None:
182
					print(" " + oformata % self.aacgmgmlats[j], end="", file=f)
183
				if self.aacgmgmlons is not None:
184
					print(" " + oformata % self.aacgmgmlons[j], end="", file=f)
185
				print("", file=f)
186
187 1
	def write_to_netcdf(self, filename, close=True):
188
		"""Write variables to netcdf files
189
190
		This function has no stream, i.e. file object, support.
191
		Uses single precision floats (32 bit) to save space.
192
193
		Parameters
194
		----------
195
		filename: str
196
			The name of the file to write the data to.
197
		"""
198
		# write the base variables first and keep the file open for appending
199 1
		ncf = scia_densities.write_to_netcdf(self, filename, close=False)
200
201 1
		if self.temperature is not None:
202 1
			ftemp = ncf.createVariable('temperature',
203
					np.dtype('float32').char, ('time', 'latitude', 'altitude'))
204 1
			ftemp.units = 'K'
205 1
			ftemp.long_name = 'temperature'
206 1
			ftemp.model = 'NRLMSIS-00'
207 1
			ftemp[0, :] = self.temperature
208
209 1
		if self.noem_no is not None:
210 1
			fnoem_no = ncf.createVariable('NOEM_density',
211
					np.dtype('float32').char, ('time', 'latitude', 'altitude'))
212 1
			fnoem_no.units = 'cm^{-3}'
213 1
			fnoem_no.long_name = 'NOEM NO number density'
214 1
			fnoem_no[0, :] = self.noem_no
215
216 1
		if self.vmr is not None:
217 1
			fvmr = ncf.createVariable('VMR',
218
					np.dtype('float32').char, ('time', 'latitude', 'altitude'))
219 1
			fvmr.units = 'ppb'
220 1
			fvmr.long_name = 'volume mixing ratio'
221 1
			fvmr[0, :] = self.vmr
222
223 1
		if self.lst is not None:
224 1
			flst = ncf.createVariable('app_lst',
225
					np.dtype('float32').char, ('time', 'latitude',))
226 1
			flst.units = 'hours'
227 1
			flst.long_name = 'apparent local solar time'
228 1
			flst[0, :] = self.lst
229
230 1
		if self.mst is not None:
231 1
			fmst = ncf.createVariable('mean_lst',
232
					np.dtype('float32').char, ('time', 'latitude',))
233 1
			fmst.units = 'hours'
234 1
			fmst.long_name = 'mean local solar time'
235 1
			fmst[0, :] = self.mst
236
237 1
		if self.sza is not None:
238 1
			fsza = ncf.createVariable('mean_sza',
239
					np.dtype('float32').char, ('time', 'latitude',))
240 1
			fsza.units = 'degrees'
241 1
			fsza.long_name = 'mean solar zenith angle'
242 1
			fsza[0, :] = self.sza
243
244 1
		if self.utchour is not None:
245 1
			futc = ncf.createVariable('utc_hour',
246
					np.dtype('float32').char, ('time', 'latitude',))
247 1
			futc.units = 'hours'
248 1
			futc.long_name = 'measurement utc time'
249 1
			futc[0, :] = self.utchour
250
251 1
		if self.utcdays is not None:
252 1
			futcd = ncf.createVariable('utc_days',
253
					np.dtype('float32').char, ('time', 'latitude',))
254 1
			futcd.long_name = 'measurement day'
255 1
			futcd.units = 'days since {0}'.format(self.date0.isoformat(sep=' '))
256 1
			futcd[0, :] = self.utcdays
257
258 1
		if self.gmlats is not None:
259 1
			fgmlats = ncf.createVariable('gm_lats',
260
					np.dtype('float32').char, ('time', 'latitude',))
261 1
			fgmlats.long_name = 'geomagnetic_latitude'
262 1
			fgmlats.model = 'IGRF'
263 1
			fgmlats.units = 'degrees_north'
264 1
			fgmlats[0, :] = self.gmlats
265
266 1
		if self.gmlons is not None:
267 1
			fgmlons = ncf.createVariable('gm_lons',
268
					np.dtype('float32').char, ('time', 'latitude',))
269 1
			fgmlons.long_name = 'geomagnetic_longitude'
270 1
			fgmlons.model = 'IGRF'
271 1
			fgmlons.units = 'degrees_east'
272 1
			fgmlons[0, :] = self.gmlons
273
274 1
		if self.aacgmgmlats is not None:
275 1
			faacgmgmlats = ncf.createVariable('aacgm_gm_lats',
276
					np.dtype('float32').char, ('time', 'latitude',))
277 1
			faacgmgmlats.long_name = 'geomagnetic_latitude'
278 1
			faacgmgmlats.model = 'AACGM'
279 1
			faacgmgmlats.units = 'degrees_north'
280 1
			faacgmgmlats[0, :] = self.aacgmgmlats
281
282 1
		if self.aacgmgmlons is not None:
283 1
			faacgmgmlons = ncf.createVariable('aacgm_gm_lons',
284
					np.dtype('float32').char, ('time', 'latitude',))
285 1
			faacgmgmlons.long_name = 'geomagnetic_longitude'
286 1
			faacgmgmlons.model = 'AACGM'
287 1
			faacgmgmlons.units = 'degrees_east'
288 1
			faacgmgmlons[0, :] = self.aacgmgmlons
289
290 1
		if close:
291 1
			ncf.close()
292
		else:
293
			return ncf
294
295 1
	def read_from_netcdf(self, filename, close=True):
296
		"""Read post-processed level 2 orbit files
297
298
		Parameters
299
		----------
300
		filename: str
301
			The name of the netcdf file.
302
		"""
303
		# read the base variables first and keep the file open for reading
304 1
		ncf = scia_densities.read_from_netcdf(self, filename, close=False)
305
306
		# additional data...
307
		# MSIS temperature
308
		try:
309
			self.temperature = ncf.variables['temperature'][:]
310
		except:
311
			pass
312
		# NOEM density
313
		try:
314
			self.noem_no = ncf.variables['NOEM_density'][:]
315
		except:
316
			pass
317
		# calculated vmr
318
		try:
319
			self.vmr = ncf.variables['VMR'][:]
320
		except:
321
			pass
322
		# apparent local solar time
323
		try:
324
			self.lst = ncf.variables['app_lst'][:]
325
		except:
326
			pass
327
		# mean local solar time
328
		try:
329
			self.mst = ncf.variables['mean_lst'][:]
330
		except:
331
			pass
332
		# mean solar zenith angle
333
		try:
334
			self.sza = ncf.variables['mean_sza'][:]
335
		except:
336
			pass
337
		# utc hours
338
		try:
339
			self.utchour = ncf.variables['utc_hour'][:]
340
		except:
341
			pass
342
		# utc days
343
		try:
344
			self.utcdays = ncf.variables['utc_days'][:]
345
		except:
346
			pass
347
348
		if close:
349
			ncf.close()
350
		else:
351
			return ncf
352
353 1
	def to_xarray(self, dateo, orbit):
354
		"""Convert the data to :class:`xarray.Dataset`
355
356
		This is a very simple approach, it dumps the data to a temporary
357
		netcdf file and reads that using :func:`xarray.open_dataset()`.
358
359
		Parameters
360
		----------
361
		dateo: float
362
			The days since the reference date at the equator
363
			crossing of the orbit. Used to set the `time`
364
			dimension of the dataset.
365
		orbit: int
366
			The SCIAMACHY/Envisat orbit number of the retrieved data.
367
368
		Returns
369
		-------
370
		dataset: xarray.Dataset
371
		"""
372 1
		import tempfile
373 1
		try:
374 1
			import xarray as xr
375
		except ImportError:
376
			print("Error: xarray not available!")
377
			return None
378 1
		with tempfile.NamedTemporaryFile() as tf:
379 1
			self.write_to_netcdf(tf.name)
380 1
			with xr.open_dataset(tf.name, decode_cf=False) as sdorb:
381 1
				sdorb = sdorb.drop(["alt_min", "alt_max", "lat_min", "lat_max"])
382 1
				sdorb["time"] = np.array([dateo], dtype=np.float32)
383 1
				sdorb["orbit"] = orbit
384 1
				sdorb.load()
385 1
		return sdorb
386
387
388 1 View Code Duplication
class scia_density_day(object):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
389
	"""SCIAMACHY daily number densities combined
390
391
	Contains a stacked version of the post-processed orbit data
392
	for multiple orbits on a day. Used to combine the results.
393
394
	Parameters
395
	----------
396
	name: str
397
		Name of the retrieved species, default: "NO".
398
		Used to name the netcdf variables appropriately.
399
	ref_date: str, optional
400
		The reference date on which to base the date calculations on.
401
		Default: "2000-01-01"
402
403
	Attributes
404
	----------
405
	alts
406
		Retrieval grid altitudes
407
	lats
408
		Retrieval grid latitudes
409
	no_dens
410
		Retrieved number densities
411
	no_errs
412
		Measurement uncertainty
413
	no_etot
414
		Total uncertainty
415
	no_rstd
416
		Relative measurement uncertainty
417
	no_akd
418
		Averaging kernel diagonal elements
419
	no_apri
420
		Prior number density
421
	temperature
422
		NRLMSISE-00 temperatures
423
	noem_no
424
		NOEM NO nuimber densities
425
	vmr
426
		NO vmr using the NRLMSISE-00 total air number densities
427
	lst
428
		Apparent local solar times
429
	mst
430
		Mean local solar times
431
	sza
432
		Solar zenith angles
433
	utchour
434
		UTC hours into measurement day
435
	utcdays
436
		Number of days since reference date
437
	gmlats
438
		IGRF-12 geomagentic latitudes
439
	gmlons
440
		IGRF-12 geomagentic longitudes
441
	aacgmgmlats
442
		AACGM geomagentic latitudes
443
	aacgmgmlons
444
		AACGM geomagentic longitudes
445
	"""
446 1
	def __init__(self, name="NO", ref_date="2000-01-01", author="unknown"):
447 1
		self.date0 = (dt.datetime.strptime(ref_date, "%Y-%m-%d")
448
					.replace(tzinfo=dt.timezone.utc))
449 1
		self.alts = None
450 1
		self.lats = None
451 1
		self.version = None
452 1
		self.data_version = None
453 1
		self.name = name
454 1
		self.author = author
455 1
		self.date = []
456 1
		self.time = []
457 1
		self.orbit = []
458 1
		self.no_dens = None
459 1
		self.no_errs = None
460 1
		self.no_etot = None
461 1
		self.no_rstd = None
462 1
		self.no_akd = None
463 1
		self.no_apri = None
464 1
		self.no_noem = None
465 1
		self.tot_dens = None
466 1
		self.no_vmr = None
467 1
		self.lons = None
468 1
		self.lst = None
469 1
		self.mst = None
470 1
		self.sza = None
471 1
		self.utchour = None
472 1
		self.utcdays = None
473 1
		self.gmlats = None
474 1
		self.gmlons = None
475 1
		self.aacgmgmlats = None
476 1
		self.aacgmgmlons = None
477
478 1
	def append(self, cdata):
479
		"""Append (stack) the data from one orbit
480
481
		Parameters
482
		----------
483
		cdata: :class:`scia_densities_pp` instance
484
			Post-processed level 2 orbital data.
485
		"""
486
		self.time.extend(cdata.time)
487
		self.date.extend(cdata.date)
488
		self.no_dens = np.ma.dstack((self.no_dens, cdata.no_dens))
489
		self.no_errs = np.ma.dstack((self.no_errs, cdata.no_errs))
490
		self.no_etot = np.ma.dstack((self.no_etot, cdata.no_etot))
491
		self.no_rstd = np.ma.dstack((self.no_rstd, cdata.no_rstd))
492
		self.no_akd = np.ma.dstack((self.no_akd, cdata.no_akd))
493
		self.no_apri = np.ma.dstack((self.no_apri, cdata.no_apri))
494
		self.no_noem = np.ma.dstack((self.no_noem, cdata.no_noem))
495
		self.tot_dens = np.ma.dstack((self.tot_dens, cdata.tot_dens))
496
		self.no_vmr = np.ma.dstack((self.no_vmr, cdata.no_vmr))
497
		self.lons = np.ma.dstack((self.lons, cdata.lons))
498
		self.lst = np.ma.dstack((self.lst, cdata.lst))
499
		self.mst = np.ma.dstack((self.mst, cdata.mst))
500
		self.sza = np.ma.dstack((self.sza, cdata.sza))
501
		self.utchour = np.ma.dstack((self.utchour, cdata.utchour))
502
		self.utcdays = np.ma.dstack((self.utcdays, cdata.utcdays))
503
		self.gmlats = np.ma.dstack((self.gmlats, cdata.gmlats))
504
		self.gmlons = np.ma.dstack((self.gmlons, cdata.gmlons))
505
		self.aacgmgmlats = np.ma.dstack((self.aacgmgmlats, cdata.aacgmgmlats))
506
		self.aacgmgmlons = np.ma.dstack((self.aacgmgmlons, cdata.aacgmgmlons))
507
508 1
	def append_data(self, date, orbit, equtime, scia_dens):
509
		"""Append (stack) the data from one orbit
510
511
		Updates the data in place.
512
513
		Parameters
514
		----------
515
		date: float
516
			Days since `ref_date` for the time coordinate
517
		orbit: int
518
			SCIAMACHY/Envisat orbit number
519
		equtime: float
520
			UTC hour into the day at the equator
521
		scia_dens: :class:`scia_densities_pp` instance
522
			The post-processed orbit data set
523
		"""
524 1
		self.version = scia_dens.version
525 1
		self.data_version = scia_dens.data_version
526 1
		self.date.append(date)
527 1
		self.orbit.append(orbit)
528 1
		self.time.append(equtime)
529 1
		_dens = scia_dens.densities
530 1
		_errs = scia_dens.dens_err_meas
531 1
		_etot = scia_dens.dens_err_tot
532 1
		_r_std = np.abs(_errs / _dens) * 100.0
533
		# This is cumbersome and error-prone but check
534
		# if we 'append' for the first time.
535
		# In that case `vstack` doesn't work so we set
536
		# the members first.
537 1
		if self.no_dens is None:
538
			# we need altitudes and latitudes only once
539 1
			self.alts = scia_dens.alts
540 1
			self.lats = scia_dens.lats
541 1
			self.no_dens = _dens[np.newaxis]
542 1
			self.no_errs = _errs[np.newaxis]
543 1
			self.no_etot = _etot[np.newaxis]
544 1
			self.no_rstd = _r_std[np.newaxis]
545 1
			self.no_akd = scia_dens.akdiag[np.newaxis]
546 1
			self.no_apri = scia_dens.apriori[np.newaxis]
547 1
			self.temperature = scia_dens.temperature[np.newaxis]
548 1
			self.no_noem = scia_dens.noem_no[np.newaxis]
549 1
			self.tot_dens = scia_dens.dens_tot[np.newaxis]
550 1
			self.no_vmr = scia_dens.vmr[np.newaxis]
551 1
			self.lons = scia_dens.lons[np.newaxis]
552 1
			self.lst = scia_dens.lst[np.newaxis]
553 1
			self.mst = scia_dens.mst[np.newaxis]
554 1
			self.sza = scia_dens.sza[np.newaxis]
555 1
			self.utchour = scia_dens.utchour[np.newaxis]
556 1
			self.utcdays = scia_dens.utcdays[np.newaxis]
557 1
			self.gmlats = scia_dens.gmlats[np.newaxis]
558 1
			self.gmlons = scia_dens.gmlons[np.newaxis]
559 1
			self.aacgmgmlats = scia_dens.aacgmgmlats[np.newaxis]
560 1
			self.aacgmgmlons = scia_dens.aacgmgmlons[np.newaxis]
561
		else:
562 1
			self.no_dens = np.ma.vstack((self.no_dens, _dens[np.newaxis]))
563 1
			self.no_errs = np.ma.vstack((self.no_errs, _errs[np.newaxis]))
564 1
			self.no_etot = np.ma.vstack((self.no_etot, _etot[np.newaxis]))
565 1
			self.no_rstd = np.ma.vstack((self.no_rstd, _r_std[np.newaxis]))
566 1
			self.no_akd = np.ma.vstack((self.no_akd, scia_dens.akdiag[np.newaxis]))
567 1
			self.no_apri = np.ma.vstack((self.no_apri, scia_dens.apriori[np.newaxis]))
568 1
			self.temperature = np.ma.vstack((self.temperature, scia_dens.temperature[np.newaxis]))
569 1
			self.no_noem = np.ma.vstack((self.no_noem, scia_dens.noem_no[np.newaxis]))
570 1
			self.tot_dens = np.ma.vstack((self.tot_dens, scia_dens.dens_tot[np.newaxis]))
571 1
			self.no_vmr = np.ma.vstack((self.no_vmr, scia_dens.vmr[np.newaxis]))
572 1
			self.lons = np.ma.vstack((self.lons, scia_dens.lons[np.newaxis]))
573 1
			self.lst = np.ma.vstack((self.lst, scia_dens.lst[np.newaxis]))
574 1
			self.mst = np.ma.vstack((self.mst, scia_dens.mst[np.newaxis]))
575 1
			self.sza = np.ma.vstack((self.sza, scia_dens.sza[np.newaxis]))
576 1
			self.utchour = np.ma.vstack((self.utchour, scia_dens.utchour[np.newaxis]))
577 1
			self.utcdays = np.ma.vstack((self.utcdays, scia_dens.utcdays[np.newaxis]))
578 1
			self.gmlats = np.ma.vstack((self.gmlats, scia_dens.gmlats[np.newaxis]))
579 1
			self.gmlons = np.ma.vstack((self.gmlons, scia_dens.gmlons[np.newaxis]))
580 1
			self.aacgmgmlats = np.ma.vstack((self.aacgmgmlats, scia_dens.aacgmgmlats[np.newaxis]))
581 1
			self.aacgmgmlons = np.ma.vstack((self.aacgmgmlons, scia_dens.aacgmgmlons[np.newaxis]))
582
583 1
	def write_to_netcdf(self, filename):
584
		"""Write variables to netcdf files
585
586
		Uses single precision floats (32 bit) to save space.
587
588
		Parameters
589
		----------
590
		filename: str
591
			The name of the file to write the data to.
592
		"""
593 1
		ncf = netcdf_file(filename, 'w', **fmtargs)
594 1
		o = np.asarray(self.orbit)
595 1
		d = np.asarray(self.date)
596 1
		t = np.asarray(self.time)
597
598 1
		if self.version is not None:
599 1
			ncf.version = self.version
600 1
		if self.data_version is not None:
601 1
			ncf.L2_data_version = self.data_version
602 1
		ncf.creation_time = dt.datetime.utcnow().strftime("%a %b %d %Y %H:%M:%S +00:00 (UTC)")
603 1
		ncf.author = self.author
604
605 1
		ncf.createDimension('time', None)
606 1
		ncf.createDimension('altitude', self.alts.size)
607 1
		ncf.createDimension('latitude', self.lats.size)
608 1
		forbit = ncf.createVariable('orbit', np.dtype('int32').char, ('time',))
609 1
		forbit.axis = 'T'
610 1
		forbit.calendar = 'standard'
611 1
		forbit.long_name = 'orbit'
612 1
		forbit.standard_name = 'orbit'
613 1
		forbit.units = 'orbit number'
614
		# the time coordinate is actually called "date" here
615 1
		ftime = ncf.createVariable('time', 'f8', ('time',))
616 1
		ftime.axis = 'T'
617 1
		ftime.calendar = 'standard'
618 1
		ftime.long_name = 'equatorial crossing time'
619 1
		ftime.standard_name = 'time'
620 1
		ftime.units = 'days since {0}'.format(self.date0.isoformat(sep=' '))
621
		#ftime.units = 'days since {0}'.format(self.date0.strftime('%Y-%m-%d %H:%M:%S%z (%Z)'))
622
		#fdate = ncf.createVariable('date', np.dtype('float32').char, ('time',))
623
		#fdate.axis = 'T'
624
		#fdate.calendar = 'standard'
625
		#fdate.long_name = 'date'
626
		#fdate.standard_name = 'date'
627
		#fdate.units = 'days since 1950-01-01 00:00:00'
628 1
		falts = ncf.createVariable('altitude', 'f8', ('altitude',))
629 1
		falts.axis = 'Z'
630 1
		falts.long_name = 'altitude'
631 1
		falts.standard_name = 'altitude'
632 1
		falts.units = 'km'
633 1
		falts.positive = 'up'
634 1
		flats = ncf.createVariable('latitude', 'f8', ('latitude',))
635 1
		flats.axis = 'Y'
636 1
		flats.long_name = 'latitude'
637 1
		flats.standard_name = 'latitude'
638 1
		flats.units = 'degrees_north'
639 1
		flons = ncf.createVariable('longitude', 'f8', ('time', 'latitude',))
640 1
		flons.long_name = 'longitude'
641 1
		flons.standard_name = 'longitude'
642 1
		flons.units = 'degrees_east'
643 1
		fdens = ncf.createVariable('%s_DENS' % self.name, 'f8', ('time', 'latitude', 'altitude'))
644 1
		fdens.units = 'cm^{-3}'
645 1
		fdens.long_name = '%s number density' % self.name
646 1
		ferrs = ncf.createVariable('%s_ERR' % self.name, 'f8', ('time', 'latitude', 'altitude'))
647 1
		ferrs.units = 'cm^{-3}'
648 1
		ferrs.long_name = '%s density measurement error' % self.name
649 1
		fetot = ncf.createVariable('%s_ETOT' % self.name, 'f8', ('time', 'latitude', 'altitude'))
650 1
		fetot.units = 'cm^{-3}'
651 1
		fetot.long_name = '%s density total error' % self.name
652 1
		frstd = ncf.createVariable('%s_RSTD' % self.name, 'f8', ('time', 'latitude', 'altitude'))
653 1
		frstd.units = '%'
654 1
		frstd.long_name = '%s relative standard deviation' % self.name
655 1
		fakd = ncf.createVariable('%s_AKDIAG' % self.name, 'f8', ('time', 'latitude', 'altitude'))
656 1
		fakd.units = '1'
657 1
		fakd.long_name = '%s averaging kernel diagonal element' % self.name
658 1
		fapri = ncf.createVariable('%s_APRIORI' % self.name, 'f8', ('time', 'latitude', 'altitude'))
659 1
		fapri.units = 'cm^{-3}'
660 1
		fapri.long_name = '%s apriori density' % self.name
661 1
		ftemp = ncf.createVariable('MSIS_Temp', 'f8', ('time', 'latitude', 'altitude'))
662 1
		ftemp.units = 'K'
663 1
		ftemp.long_name = 'MSIS temperature'
664 1
		ftemp.model = 'NRLMSIS-00'
665 1
		fnoem = ncf.createVariable('%s_NOEM' % self.name, 'f8', ('time', 'latitude', 'altitude'))
666 1
		fnoem.units = 'cm^{-3}'
667 1
		fnoem.long_name = 'NOEM %s number density' % self.name
668 1
		fdens_tot = ncf.createVariable('MSIS_Dens', 'f8', ('time', 'latitude', 'altitude'))
669 1
		fdens_tot.units = 'cm^{-3}'
670 1
		fdens_tot.long_name = 'MSIS total number density'
671 1
		fdens_tot.model = 'NRLMSIS-00'
672 1
		fvmr = ncf.createVariable('%s_VMR' % self.name, 'f8', ('time', 'latitude', 'altitude'))
673 1
		fvmr.units = 'ppb'
674 1
		fvmr.long_name = '%s volume mixing ratio' % self.name
675 1
		flst = ncf.createVariable('app_LST', 'f8', ('time', 'latitude'))
676 1
		flst.units = 'hours'
677 1
		flst.long_name = 'apparent local solar time'
678 1
		fmst = ncf.createVariable('mean_LST', 'f8', ('time', 'latitude'))
679 1
		fmst.units = 'hours'
680 1
		fmst.long_name = 'mean local solar time'
681 1
		fsza = ncf.createVariable('mean_SZA', 'f8', ('time', 'latitude'))
682 1
		fsza.units = 'degrees'
683 1
		fsza.long_name = 'solar zenith angle at mean altitude'
684 1
		futc = ncf.createVariable('UTC', 'f8', ('time', 'latitude'))
685 1
		futc.units = 'hours'
686 1
		futc.long_name = 'measurement utc time'
687 1
		futcd = ncf.createVariable('utc_days', 'f8', ('time', 'latitude'))
688 1
		futcd.long_name = 'measurement utc day'
689 1
		futcd.units = 'days since {0}'.format(self.date0.isoformat(sep=' '))
690
691 1
		fgmlats = ncf.createVariable('gm_lats', 'f8', ('time', 'latitude',))
692 1
		fgmlats.long_name = 'geomagnetic_latitude'
693 1
		fgmlats.model = 'IGRF'
694 1
		fgmlats.units = 'degrees_north'
695
696 1
		fgmlons = ncf.createVariable('gm_lons', 'f8', ('time', 'latitude',))
697 1
		fgmlons.long_name = 'geomagnetic_longitude'
698 1
		fgmlons.model = 'IGRF'
699 1
		fgmlons.units = 'degrees_east'
700
701 1
		faacgmgmlats = ncf.createVariable('aacgm_gm_lats', 'f8', ('time', 'latitude',))
702 1
		faacgmgmlats.long_name = 'geomagnetic_latitude'
703 1
		faacgmgmlats.model = 'AACGM'
704 1
		faacgmgmlats.units = 'degrees_north'
705
706 1
		faacgmgmlons = ncf.createVariable('aacgm_gm_lons', 'f8', ('time', 'latitude',))
707 1
		faacgmgmlons.long_name = 'geomagnetic_longitude'
708 1
		faacgmgmlons.model = 'AACGM'
709 1
		faacgmgmlons.units = 'degrees_east'
710
711 1
		forbit[:] = o
712 1
		ftime[:] = d
713 1
		falts[:] = self.alts
714 1
		flats[:] = self.lats
715 1
		flons[:] = self.lons
716 1
		fdens[:] = np.ma.atleast_3d(self.no_dens[:])
717 1
		ferrs[:] = np.ma.atleast_3d(self.no_errs[:])
718 1
		fetot[:] = np.ma.atleast_3d(self.no_etot[:])
719 1
		frstd[:] = np.ma.atleast_3d(self.no_rstd[:])
720 1
		fakd[:] = np.ma.atleast_3d(self.no_akd[:])
721 1
		fapri[:] = np.ma.atleast_3d(self.no_apri[:])
722 1
		ftemp[:] = np.ma.atleast_3d(self.temperature[:])
723 1
		fnoem[:] = np.ma.atleast_3d(self.no_noem[:])
724 1
		fdens_tot[:] = np.ma.atleast_3d(self.tot_dens[:])
725 1
		fvmr[:] = np.ma.atleast_3d(self.no_vmr[:])
726 1
		flst[:] = np.ma.atleast_2d(self.lst[:])
727 1
		fmst[:] = np.ma.atleast_2d(self.mst[:])
728 1
		fsza[:] = np.ma.atleast_2d(self.sza[:])
729 1
		futc[:] = np.ma.atleast_2d(self.utchour[:])
730 1
		futcd[:] = np.ma.atleast_2d(self.utcdays[:])
731 1
		fgmlats[:] = np.ma.atleast_2d(self.gmlats[:])
732 1
		fgmlons[:] = np.ma.atleast_2d(self.gmlons[:])
733 1
		faacgmgmlats[:] = np.ma.atleast_2d(self.aacgmgmlats[:])
734 1
		faacgmgmlons[:] = np.ma.atleast_2d(self.aacgmgmlons[:])
735 1
		ncf.close()
736
737 1
	def to_xarray(self):
738
		"""Convert the combined orbit data to :class:`xarray.Dataset`
739
740
		Exports the data using the same data variables as
741
		when writing to netcdf.
742
743
		Returns
744
		-------
745
		dataset: xarray.Dataset
746
		"""
747 1
		try:
748 1
			import xarray as xr
749
		except ImportError:
750
			print("Error: xarray not available!")
751
			return None
752 1
		o = np.asarray(self.orbit)
753 1
		d = np.asarray(self.date)
754
755 1
		xr_dens = xr.DataArray(self.no_dens, coords=[d, self.lats, self.alts],
756
				dims=["time", "latitude", "altitude"],
757
				attrs={"units": "cm^{-3}",
758
						"long_name": "{0} number density".format(self.name)},
759
				name="{0}_DENS".format(self.name))
760
761 1
		xr_errs = xr.DataArray(self.no_errs, coords=[d, self.lats, self.alts],
762
				dims=["time", "latitude", "altitude"],
763
				attrs={"units": "cm^{-3}",
764
						"long_name": "{0} density measurement error".format(self.name)},
765
				name="{0}_ERR".format(self.name))
766
767 1
		xr_etot = xr.DataArray(self.no_etot, coords=[d, self.lats, self.alts],
768
				dims=["time", "latitude", "altitude"],
769
				attrs={"units": "cm^{-3}",
770
						"long_name": "{0} density total error".format(self.name)},
771
				name="{0}_ETOT".format(self.name))
772
773 1
		xr_rstd = xr.DataArray(self.no_rstd, coords=[d, self.lats, self.alts],
774
				dims=["time", "latitude", "altitude"],
775
				attrs=dict(units='%',
776
						long_name='{0} relative standard deviation'.format(self.name)),
777
				name="{0}_RSTD".format(self.name))
778
779 1
		xr_akd = xr.DataArray(self.no_akd, coords=[d, self.lats, self.alts],
780
				dims=["time", "latitude", "altitude"],
781
				attrs=dict(units='1',
782
						long_name='{0} averaging kernel diagonal element'.format(self.name)),
783
				name="{0}_AKDIAG".format(self.name))
784
785 1
		xr_apri = xr.DataArray(self.no_apri, coords=[d, self.lats, self.alts],
786
				dims=["time", "latitude", "altitude"],
787
				attrs=dict(units='cm^{-3}', long_name='{0} apriori density'.format(self.name)),
788
				name="{0}_APRIORI".format(self.name))
789
790 1
		xr_noem = xr.DataArray(self.no_noem, coords=[d, self.lats, self.alts],
791
				dims=["time", "latitude", "altitude"],
792
				attrs=dict(units='cm^{-3}', long_name='NOEM {0} number density'.format(self.name)),
793
				name="{0}_NOEM".format(self.name))
794
795 1
		xr_vmr = xr.DataArray(self.no_vmr, coords=[d, self.lats, self.alts],
796
				dims=["time", "latitude", "altitude"],
797
				attrs=dict(units='ppb', long_name='{0} volume mixing ratio'.format(self.name)),
798
				name="{0}_VMR".format(self.name))
799
800 1
		xr_dtot = xr.DataArray(self.tot_dens, coords=[d, self.lats, self.alts],
801
				dims=["time", "latitude", "altitude"],
802
				attrs=dict(units='cm^{-3}', long_name='MSIS total number density',
803
					model='NRLMSIS-00'),
804
				name="MSIS_Dens")
805
806 1
		xr_temp = xr.DataArray(self.temperature, coords=[d, self.lats, self.alts],
807
				dims=["time", "latitude", "altitude"],
808
				attrs=dict(units='K', long_name='MSIS temperature',
809
						model="NRLMSIS-00"),
810
				name="MSIS_Temp")
811
812 1
		xr_lons = xr.DataArray(self.lons, coords=[d, self.lats],
813
				dims=["time", "latitude"],
814
				attrs=dict(long_name='longitude', standard_name='longitude',
815
						units='degrees_east'),
816
				name='longitude')
817
818 1
		xr_lst = xr.DataArray(self.lst, coords=[d, self.lats],
819
				dims=["time", "latitude"],
820
				attrs=dict(units='hours', long_name='apparent local solar time'),
821
				name="app_LST")
822
823 1
		xr_mst = xr.DataArray(self.mst, coords=[d, self.lats],
824
				dims=["time", "latitude"],
825
				attrs=dict(units='hours', long_name='mean local solar time'),
826
				name="mean_LST")
827
828 1
		xr_sza = xr.DataArray(self.sza, coords=[d, self.lats],
829
				dims=["time", "latitude"],
830
				attrs=dict(units='degrees',
831
						long_name='solar zenith angle at mean altitude'),
832
				name="mean_SZA")
833
834 1
		xr_utch = xr.DataArray(self.utchour, coords=[d, self.lats],
835
				dims=["time", "latitude"],
836
				attrs=dict(units='hours',
837
						long_name='measurement utc time'),
838
				name="UTC")
839
840 1
		xr_utcd = xr.DataArray(self.utcdays, coords=[d, self.lats],
841
				dims=["time", "latitude"],
842
				attrs=dict(units='days since {0}'.format(self.date0.isoformat(sep=' ')),
843
						long_name='measurement utc day'),
844
				name="utc_days")
845
846 1
		xr_gmlats = xr.DataArray(self.gmlats, coords=[d, self.lats],
847
				dims=["time", "latitude"],
848
				attrs=dict(long_name='geomagnetic_latitude',
849
						model='IGRF', units='degrees_north'),
850
				name="gm_lats")
851
852 1
		xr_gmlons = xr.DataArray(self.gmlons, coords=[d, self.lats],
853
				dims=["time", "latitude"],
854
				attrs=dict(long_name='geomagnetic_longitude',
855
						model='IGRF', units='degrees_east'),
856
				name="gm_lons")
857
858 1
		xr_aacgmgmlats = xr.DataArray(self.aacgmgmlats, coords=[d, self.lats],
859
				dims=["time", "latitude"],
860
				attrs=dict(long_name='geomagnetic_latitude',
861
						model='AACGM', units='degrees_north'),
862
				name="aacgm_gm_lats")
863
864 1
		xr_aacgmgmlons = xr.DataArray(self.aacgmgmlons, coords=[d, self.lats],
865
				dims=["time", "latitude"],
866
				attrs=dict(long_name='geomagnetic_longitude',
867
						model='AACGM', units='degrees_east'),
868
				name="aacgm_gm_lons")
869
870 1
		xr_orbit = xr.DataArray(o, coords=[d], dims=["time"],
871
				attrs=dict(axis='T', calendar='standard', long_name='orbit',
872
						standard_name='orbit', units='orbit number'),
873
				name="orbit")
874
875 1
		xr_ds = xr.Dataset({da.name: da for da in
876
				[xr_dens, xr_errs, xr_etot, xr_rstd, xr_akd, xr_apri, xr_noem,
877
					xr_vmr, xr_dtot, xr_temp, xr_lons, xr_lst, xr_mst, xr_sza,
878
					xr_utch, xr_utcd, xr_gmlats, xr_gmlons, xr_aacgmgmlats,
879
					xr_aacgmgmlons, xr_orbit]})
880
881 1
		xr_ds["time"].attrs = dict(axis='T', calendar='standard',
882
						long_name='equatorial crossing time',
883
						standard_name='time',
884
						units='days since {0}'.format(self.date0.isoformat(sep=' ')))
885
886 1
		xr_ds["altitude"].attrs = dict(axis='Z', long_name='altitude',
887
				standard_name='altitude', units='km', positive='up')
888
889 1
		xr_ds["latitude"].attrs = dict(axis='Y', long_name='latitude',
890
				standard_name='latitude', units='degrees_north')
891
892 1
		if self.version is not None:
893 1
			xr_ds.attrs["version"] = self.version
894 1
		if self.data_version is not None:
895 1
			xr_ds.attrs["L2_data_version"] = self.data_version
896 1
		xr_ds.attrs["creation_time"] = dt.datetime.utcnow().strftime("%a %b %d %Y %H:%M:%S +00:00 (UTC)")
897 1
		xr_ds.attrs["author"] = self.author
898
899
		return xr_ds
900