atmospheric_lidar/diva.py

Fri, 15 Dec 2017 13:33:10 +0200

author
Iannis <ulalume3@yahoo.com>
date
Fri, 15 Dec 2017 13:33:10 +0200
changeset 113
b5bbfee2a898
parent 112
07bb7a219314
child 114
d11a94f03753
permissions
-rw-r--r--

* Ancillary variable group

* Example of analog and photon-counting channel parameters.

i@108 1 """ This is a class for experimenting with the new DIVA / EARLINET NetCDF file format.
i@108 2
i@108 3 In the long run, this should be places as a method in BaseLidarMeasurement class. For now it is kept
i@108 4 separately not to interfere with normal development.
i@108 5 """
i@108 6 import netCDF4 as netcdf
i@108 7 import pyyaml
i@108 8 import datetime
i@108 9 import os
i@108 10
i@108 11 from .generic import BaseLidarMeasurement
i@108 12
i@108 13
i@108 14 class DivaOutput(BaseLidarMeasurement):
i@108 15
i@108 16 def save_as_diva(self, output_path, parameter_file):
i@108 17 """ Save the current data in the 'draft' DIVA format. """
i@108 18
i@108 19 with open(parameter_file, 'r') as f:
i@108 20 parameters = pyyaml.load(f)
i@108 21
i@108 22 global_parameters = parameters['global_parameters'] # Shortcut
ulalume3@112 23 channels = parameters['channels']
i@108 24
i@108 25 iso_date = datetime.datetime.utcnow().isoformat()
i@108 26 python_file_name = os.path.basename(__file__)
i@108 27
i@108 28 with netcdf.Dataset(output_path, 'w', format="NETCDF4") as f:
i@108 29
i@108 30 # Global attributes
i@108 31 f.title = global_parameters['title']
i@108 32 f.source = global_parameters['source']
i@108 33 f.institution = global_parameters['institution']
i@108 34 f.references = global_parameters['references']
i@108 35 f.location = global_parameters['location']
i@108 36 f.data_version = global_parameters['data_version']
i@108 37 f.conversion_date = iso_date
i@108 38 f.comment = global_parameters['comment']
i@108 39 f.Conventions = global_parameters['Conventions']
i@108 40 f.history = global_parameters['history'].format(date=iso_date, file=python_file_name)
i@108 41 f.featureType = "timeSeriesProfile"
i@108 42
ulalume3@112 43 # Top level dimensions
ulalume3@112 44 f.createDimension('name_strlen', size=40)
ulalume3@112 45 f.createDimension('nv', size=2)
ulalume3@112 46
i@108 47 # Top level variables
i@108 48 latitude = f.createVariable('latitude', datatype='f4')
i@108 49 latitude.standard_name = 'latitude'
i@108 50 latitude.long_name = 'system latitude'
i@108 51 latitude.units = 'degrees_north'
i@108 52
i@108 53 longitude = f.createVariable('longitude', datatype='f4')
i@108 54 longitude.standard_name = 'longitude'
i@108 55 longitude.long_name = 'system longitude'
i@108 56 longitude.units = 'degrees_east'
i@108 57
i@108 58 laser_angle = f.createVariable('laser_zenith_angle', datatype='f4')
i@108 59 laser_angle.standard_name = 'sensor_zenith_angle'
i@108 60 laser_angle.long_name = 'zenith angle of emitted laser'
i@108 61 laser_angle.units = 'degree'
i@108 62
ulalume3@112 63 laser_azimuth = f.createVariable('laser_azimuth_angle', datatype='f4')
ulalume3@112 64 laser_azimuth.standard_name = 'sensor_azimuth_angle'
ulalume3@112 65 laser_azimuth.long_name = 'azimuth angle of emitted laser'
ulalume3@112 66 laser_azimuth.units = 'degree'
ulalume3@112 67 laser_azimuth.comment = 'Based on North. Optional'
i@108 68
i@108 69 altitude = f.createVariable('altitude', datatype='f4')
i@108 70 altitude.standard_name = 'altitude'
i@108 71 altitude.long_name = 'system altitude'
i@108 72 altitude.units = 'm'
i@108 73
ulalume3@113 74 # Optional ancillary group
ulalume3@113 75 ancillary = f.createGroup('ancillary')
ulalume3@113 76 ancillary.featureType = "timeSeries"
ulalume3@113 77
ulalume3@113 78 ancillary.createDimension('time', size=None)
ulalume3@113 79
ulalume3@113 80 time = ancillary.createVariable('time', datatype='f8', dimensions=('time',))
ulalume3@113 81 time.long_name = 'time'
ulalume3@113 82 time.units = 'seconds since 1970-01-01 00:00'
ulalume3@113 83 time.standard_name = 'time'
ulalume3@113 84
ulalume3@113 85 temperature = ancillary.createVariable('air_temperature', datatype='f8', dimensions=('time',))
ulalume3@113 86 temperature.long_name = 'air temperature at instrument level'
ulalume3@113 87 temperature.units = 'K'
ulalume3@113 88 temperature.standard_name = 'air_temperature'
ulalume3@113 89
ulalume3@113 90 pressure = ancillary.createVariable('air_pressure', datatype='f8', dimensions=('time',))
ulalume3@113 91 pressure.long_name = 'air pressure at instrument level'
ulalume3@113 92 pressure.units = 'hPa'
ulalume3@113 93 pressure.standard_name = 'air_pressure'
ulalume3@113 94
ulalume3@113 95 # Create a separate group for each channel
ulalume3@112 96 for channel_name, channel_parameters in channels.iteritems():
i@108 97
ulalume3@112 98 group_name = "channel_{0}".format(channel_name) # Give channels groups a standard name
ulalume3@112 99 g = f.createGroup(group_name)
ulalume3@112 100 g.long_name = channel_parameters['long_name']
ulalume3@112 101 g.detector_manufacturer = channel_parameters['long_name'] # Optional
ulalume3@112 102 g.detector_model = channel_parameters['detector_model']
ulalume3@112 103 g.daq_manufacturer = channel_parameters['daq_manufacturer']
ulalume3@112 104 g.daq_model = channel_parameters['daq_model']
i@108 105
ulalume3@112 106 # Dimensions
ulalume3@112 107 g.createDimension('profile', size=None) # Infinite dimension
ulalume3@112 108 g.createDimension('range', len(self.range))
i@108 109
ulalume3@112 110 # Variables
ulalume3@112 111 name = g.createVariable('channel_id', dimensions=('name_strlen',))
ulalume3@112 112 name.cf_role = 'timeseries_id'
ulalume3@112 113 name.long_name = 'channel identification'
i@108 114
ulalume3@112 115 laser_rep_rate = g.createVariable('laser_repetition_rate')
ulalume3@112 116 laser_rep_rate.long_name = 'nominal laser repetition rate'
ulalume3@112 117 laser_rep_rate.units = 'Hz'
i@108 118
ulalume3@112 119 emission_energy = g.createVariable('emission_energy', datatype='f8', dimensions=('profile',))
ulalume3@112 120 emission_energy.long_name = 'emission energy per pulse'
ulalume3@112 121 emission_energy.units = 'mJ'
ulalume3@112 122 emission_energy.standard_name = 'radiation_wavelength'
ulalume3@112 123 emission_energy.comment = "could be scalar, if value is nominal."
i@108 124
ulalume3@112 125 emission_pol = g.createVariable('emission_polarization', datatype='b')
ulalume3@112 126 emission_pol.long_name = 'nominal emission poalrization'
ulalume3@112 127 emission_pol.flag_values = '0b 1b 2b'
ulalume3@112 128 emission_pol.flag_meanings = 'linear circular none'
i@108 129
ulalume3@112 130 fov = g.createVariable('fov', datatype='f4')
ulalume3@112 131 fov.long_name = 'channel field of view full angle'
ulalume3@112 132 fov.units = 'mrad'
ulalume3@112 133 fov.comment = 'simulated'
ulalume3@112 134
ulalume3@112 135 detector_type = g.createVariable('detector_type', datatype='b')
ulalume3@112 136 detector_type.long_name = 'detector type'
ulalume3@112 137 detector_type.flag_values = '0b 1b'
ulalume3@112 138 detector_type.flag_meanings = 'PMT APD'
i@108 139
ulalume3@112 140 detection_mode = g.createVariable('detection_mode', datatype='b')
ulalume3@112 141 detection_mode.long_name = 'detection mode'
ulalume3@112 142 detection_mode.flag_values = '0b 1b'
ulalume3@112 143 detection_mode.flag_meanings = 'analog photon_counting'
ulalume3@112 144
ulalume3@112 145 detection_cw = g.createVariable('detection_wavelength', datatype='f8')
ulalume3@112 146 detection_cw.long_name = 'center wavelength of detection filters'
ulalume3@112 147 detection_cw.units = 'nm'
ulalume3@112 148 detection_cw.standard_name = 'sensor_band_central_radiation_wavelength'
i@108 149
ulalume3@112 150 detection_fwhm = g.createVariable('detection_fwhm', datatype='f8')
ulalume3@112 151 detection_fwhm.long_name = 'FWHM of detection filters'
ulalume3@112 152 detection_fwhm.units = 'nm'
i@108 153
ulalume3@112 154 detection_pol = g.createVariable('detection_polarization', datatype='b')
ulalume3@112 155 detection_pol.long_name = 'nominal detection poalrization'
ulalume3@112 156 detection_pol.flag_values = '0b 1b 2b'
ulalume3@112 157 detection_pol.flag_meanings = 'linear circular none'
ulalume3@112 158
ulalume3@112 159 polarizer_angle = g.createVariable('polarizer_angle', datatype='f4', dimensions=('profile', ), zlib=True)
ulalume3@112 160 polarizer_angle.long_name = 'polarizer angle in respect to laser plane of polarization'
ulalume3@112 161 polarizer_angle.units = 'degree'
ulalume3@112 162 polarizer_angle.comments = 'Optional'
i@108 163
ulalume3@112 164 dead_time_model = g.createVariable('dead_time_model', datatype='b')
ulalume3@112 165 dead_time_model.long_name = 'optimal dead time model of detection system'
ulalume3@112 166 dead_time_model.flag_values = '0b 1b 2b'
ulalume3@112 167 dead_time_model.flag_meanings = 'paralyzable non_paralyzable other'
i@108 168
ulalume3@112 169 dead_time = g.createVariable('dead_time', datatype='f8')
ulalume3@112 170 dead_time.long_name = 'dead time value'
ulalume3@112 171 dead_time.units = 'ns'
ulalume3@112 172 dead_time.comment = "Manufacturer. Source of the value."
ulalume3@112 173
ulalume3@112 174 bin_length = g.createVariable('bin_length', datatype='f4')
ulalume3@112 175 bin_length.long_name = "time duration of each bin"
ulalume3@112 176 bin_length.units = 'ns'
ulalume3@112 177
ulalume3@112 178 detector_voltage = g.createVariable('detector_voltage', datatype='f4', dimensions=('profile',), zlib=True)
ulalume3@112 179 detector_voltage.long_name = 'detector voltage'
ulalume3@112 180 detector_voltage.units = 'V'
ulalume3@112 181 detector_voltage.coordinates = "time"
i@108 182
ulalume3@112 183 discriminator = g.createVariable('discriminator', datatype='f8', dimensions=('profiles',))
ulalume3@112 184 discriminator.long_name = 'discriminator level'
ulalume3@112 185 discriminator.units = ''
ulalume3@112 186
ulalume3@112 187 adc_range = g.createVariable('adc_range', datatype='f4', dimensions=('profile',),
ulalume3@112 188 zlib=True)
ulalume3@112 189 adc_range.long_name = 'analog-to-digital converter range'
ulalume3@112 190 adc_range.units = 'mV'
ulalume3@112 191 adc_range.coordinates = "time"
i@108 192
ulalume3@112 193 adc_bits = g.createVariable('adc_bits', datatype='i4', dimensions=('profile',),
ulalume3@112 194 zlib=True)
ulalume3@112 195 adc_bits.long_name = 'analog-to-digital converter bits'
ulalume3@112 196 adc_bits.coordinates = "time"
ulalume3@112 197
ulalume3@112 198 pulses = g.createVariable('pulses', datatype='i4', dimensions=('profile',),
ulalume3@112 199 zlib=True)
ulalume3@112 200 pulses.long_name = 'accumulated laser pulses per record'
ulalume3@112 201 pulses.coordinates = "time"
i@108 202
ulalume3@112 203 nd_filter = g.createVariable('nd_filter_od', datatype='f8', dimensions=('profile',))
ulalume3@112 204 nd_filter.long_name = "neutral density filter optical depth "
ulalume3@112 205 nd_filter.coordinates = "time"
i@108 206
ulalume3@112 207 emission_delay = g.createVariable('emission_delay', datatype='f4')
ulalume3@112 208 emission_delay.long_name = "pulse emission difference from channel trigger"
ulalume3@112 209 emission_delay.units = 'ns'
ulalume3@112 210 emission_delay.comments = 'Positive values for pre-trigger systems. Negative for trigger delay.'
i@108 211
ulalume3@112 212 time = g.createVariable('time', datatype='f8', dimensions=('profile',),
ulalume3@112 213 zlib=True)
ulalume3@112 214 time.long_name = 'profile start time '
ulalume3@112 215 time.units = "seconds since 1970-01-01 00:00:00"
ulalume3@112 216 time.standard_name = "time"
ulalume3@112 217 time.bounds = "time_bnds"
ulalume3@112 218 g.createVariable('time_bnds', datatype='f8', dimensions=('profile', 'nv'), zlib=True)
i@108 219
ulalume3@112 220 bin_time = g.createVariable('bin_time', datatype='f4', dimensions=('range',),
ulalume3@112 221 zlib=True) # Use name 'bin_range' to avoid conflict with built-in range
ulalume3@112 222 bin_time.long_name = 'bin start time since trigger'
ulalume3@112 223 bin_time.units = "ns"
i@108 224
ulalume3@112 225 signal = g.createVariable('signal', datatype='i4', dimensions=('profile', 'range'),
ulalume3@112 226 zlib=True)
ulalume3@112 227 signal.long_name = 'signal'
ulalume3@112 228 signal.units = channel_parameters['signal_units']
ulalume3@112 229 signal.coordinates = "time"
ulalume3@112 230 signal.ancillary_variables = "signal_stddev"
ulalume3@112 231
ulalume3@112 232 # If measured
ulalume3@112 233 signal_stddev = g.createVariable('signal', datatype='i4', dimensions=('profile', 'range'),
ulalume3@112 234 zlib=True)
ulalume3@112 235 signal_stddev.long_name = 'signal standard deviation'
ulalume3@112 236 signal_stddev.units = channel_parameters['signal_units']
ulalume3@112 237 signal_stddev.coordinates = "time"
ulalume3@112 238
ulalume3@112 239 # Assign variables
ulalume3@112 240 # TBD

mercurial