IODA Bundle
goes.py
Go to the documentation of this file.
1 #
2 # goes.py
3 #
4 # This class loads, calculates, filters, and makes accessible the variables and attributes required by the
5 # GoesConverter class for a single GOES-16 or GOES-17 LB1 ABI channel (1-16). The brightness temperature and reflectance
6 # factor calculations and down-sampling / subsampling techniques used in this class are derived from sections 3.4.1.2,
7 # 3.4.1.3, and 3.4.3 in the GOES-R Advanced Baseline Imager (ABI) Algorithm Theoretical Basis Document For Cloud
8 # and Moisture Imagery Product (CMIP) Version 3.0 July 30, 2012. The calculations for the propagation of standard error
9 # are from section 2.5.5 of the NIST/SEMATECH e-Handbook of Statistical Methods available at
10 # https://www.itl.nist.gov/div898/handbook/mpc/section5/mpc55.htm. GOES ABI channels 1, 3, and 5 are subsampled from
11 # 1km to 2km resolution and ABI channel 2 is subsampled from 0.5km to 2km resolution using methods in this class. This
12 # class includes two subsampling and two down-sampling functions. The down-sampling functions are not currently used
13 # due to the long computation time required. The preferred subsampling method which is used by this class is called
14 # "_subsample".
15 #
16 import os
17 from enum import Enum
18 import numpy as np
19 from netCDF4 import Dataset
20 from numpy import ma
21 from solo.date import Date
22 
23 
24 class Goes:
25 
26  def __init__(self, input_file_path):
27  """
28  Constructor
29  input_file_path - a GOES-16 or GOES-17 raw data file for a single ABI channel
30  """
31  self._input_file_path_input_file_path = input_file_path
32  self._get_metadata_from_input_file_path_get_metadata_from_input_file_path()
33  self._rad_data_array_rad_data_array = None
34  self._dqf_data_array_dqf_data_array = None
35  self._obsvalue_rf_data_array_obsvalue_rf_data_array = None
36  self._obsvalue_bt_data_array_obsvalue_bt_data_array = None
37  self._obserror_rf_data_array_obserror_rf_data_array = None
38  self._obserror_bt_data_array_obserror_bt_data_array = None
39 
41  """
42  Creates a dictionary of file metadata from input_file_path
43  """
44  self._metadata_dict_metadata_dict = {'instrument': 'ABI',
45  'processing_level': 'L1b',
46  'product_acronym': 'Rad'}
47  metadata_array = os.path.splitext(os.path.basename(self._input_file_path_input_file_path))[0].split('_')
48  self._metadata_dict_metadata_dict['system_environment'] = metadata_array[0]
49  self._metadata_dict_metadata_dict['abi_sector_type'] = Goes._string_to_abisectortype(metadata_array[1])
50  self._metadata_dict_metadata_dict['abi_mode'] = Goes._string_to_abimode(metadata_array[1])
51  self._metadata_dict_metadata_dict['abi_channel'] = int(metadata_array[1][-2:])
52  self._metadata_dict_metadata_dict['platform_identifier'] = metadata_array[2]
53  self._metadata_dict_metadata_dict['start_date'] = Date(metadata_array[3][1:-1])
54  self._metadata_dict_metadata_dict['end_date'] = Date(metadata_array[4][1:-1])
55  self._metadata_dict_metadata_dict['creation_date'] = Date(metadata_array[5][1:-1])
56 
57  def _open(self):
58  """
59  Opens a netCDF4 dataset using input_file_path.
60  """
61  self._input_dataset_input_dataset = Dataset(self._input_file_path_input_file_path, 'r')
62 
64  """
65  Creates a local yaw_flip_flag variable.
66  """
67  self._yaw_flip_flag_yaw_flip_flag = self._input_dataset_input_dataset.variables['yaw_flip_flag'][0]
68 
70  """
71  Creates a local kappa0 variable.
72  """
73  self._kappa0_kappa0 = ma.getdata(self._input_dataset_input_dataset.variables['kappa0'][0])
74 
76  """
77  Creates a local variables for the four Planck constants.
78  """
79  self._planck_bc1_planck_bc1 = self._input_dataset_input_dataset.variables['planck_bc1'][0]
80  self._planck_bc2_planck_bc2 = self._input_dataset_input_dataset.variables['planck_bc2'][0]
81  self._planck_fk1_planck_fk1 = self._input_dataset_input_dataset.variables['planck_fk1'][0]
82  self._planck_fk2_planck_fk2 = self._input_dataset_input_dataset.variables['planck_fk2'][0]
83 
85  """
86  Creates a local variable for the standard deviation of radiance for only valid pixels.
87  """
88  self._std_dev_radiance_value_of_valid_pixels_std_dev_radiance_value_of_valid_pixels = \
89  self._input_dataset_input_dataset.variables['std_dev_radiance_value_of_valid_pixels'][0]
90 
92  """
93  Creates a local variable of valid pixel counts.
94  """
95  self._valid_pixel_count_valid_pixel_count = self._input_dataset_input_dataset.variables['valid_pixel_count'][0]
96 
98  """
99  Creates a local data array for the DQF variable.
100  """
101  self._dqf_data_array_dqf_data_array = ma.getdata(self._input_dataset_input_dataset.variables['DQF'][:].real)
102 
104  """
105  Creates a local data array for the Rad variable.
106  """
107  self._rad_data_array_rad_data_array = ma.getdata(self._input_dataset_input_dataset.variables['Rad'][:].real)
108 
109  @staticmethod
110  def _subsample(rad_data_array, dqf_data_array, increment):
111  """
112  Returns the dqf and rad data arrays after being subsampled with the given increment (aka step)
113  between data points using array slicing.
114  """
115  current_dim = len(rad_data_array)
116  rad_data_array = np.asarray(rad_data_array)
117  new_rad_data_array = rad_data_array[0:current_dim:increment, 0:current_dim:increment]
118  dqf_data_array = np.asarray(dqf_data_array)
119  new_dqf_data_array = dqf_data_array[0:current_dim:increment, 0:current_dim:increment]
120  return new_rad_data_array, new_dqf_data_array
121 
122  @staticmethod
123  def _subsample2(rad_data_array, dqf_data_array, increment):
124  """
125  Returns the dqf and rad data arrays after being subsampled with the given increment (aka step)
126  between data points using array looping.
127  """
128  current_dim = len(rad_data_array)
129  new_dim = int(current_dim / increment)
130  new_rad_data_array = [[0] * new_dim] * new_dim
131  new_dqf_data_array = [[0] * new_dim] * new_dim
132 
133  k, n = 0, 0
134  for i in range(0, current_dim, increment):
135  for j in range(0, current_dim, increment):
136  new_rad_data_array[k][n] = rad_data_array[i][j]
137  new_dqf_data_array[k][n] = dqf_data_array[i][j]
138  n += 1
139  if n == new_dim:
140  n = 0
141  k += 1
142 
143  return new_rad_data_array, new_dqf_data_array
144 
145  @staticmethod
146  def _downscale_1km_to_2km(rad_data_array, dqf_data_array):
147  """
148  Returns the dqf and rad data arrays after being down-sampled from 1km to 2km resolution taking account for
149  pixel averaging and the DQF valid pixel flags. This routine is computationally expensive.
150  """
151  current_dim = len(rad_data_array)
152  increment = 2
153  new_dim = int(current_dim / increment)
154  new_rad_data_array = [[0] * new_dim] * new_dim
155  new_dqf_data_array = [[0] * new_dim] * new_dim
156 
157  k, n = 0, 0
158  for i in range(0, current_dim, increment):
159  for j in range(0, current_dim, increment):
160  point_array = [(i, j), (i + 1, j),
161  (i, j + 1), (i + 1, j + 1)]
162  mean_array = []
163  for point in point_array:
164  dqf = dqf_data_array[point[0]][point[1]]
165  if dqf == 0 or dqf == 1:
166  mean_array.append(rad_data_array[point[0]][point[1]])
167  if len(mean_array) != 0:
168  new_rad_data_array[k][n] = np.mean(mean_array)
169  new_dqf_data_array[k][n] = 0
170  else:
171  new_rad_data_array[k][n] = -999
172  new_dqf_data_array[k][n] = -1
173  n += 1
174  if n == new_dim:
175  n = 0
176  k += 1
177 
178  return new_rad_data_array, new_dqf_data_array
179 
180  @staticmethod
181  def _downscale_05km_to_2km(rad_data_array, dqf_data_array):
182  """
183  Returns the dqf and rad data arrays after being down-sampled from 0.5km to 2km resolution taking account for
184  pixel averaging and the DQF valid pixel flags. This routine is computationally expensive.
185  """
186  current_dim = len(rad_data_array)
187  increment = 4
188  new_dim = int(current_dim / increment)
189  new_rad_data_array = [[0] * new_dim] * new_dim
190  new_dqf_data_array = [[0] * new_dim] * new_dim
191 
192  k, n = 0, 0
193  for i in range(0, current_dim, increment):
194  for j in range(0, current_dim, increment):
195  point_array = [(i, j), (i + 1, j), (i + 2, j), (i + 3, j),
196  (i, j + 1), (i + 1, j + 1), (i + 2, j + 1), (i + 3, j + 1),
197  (i, j + 2), (i + 1, j + 2), (i + 2, j + 2), (i + 3, j + 2),
198  (i, j + 3), (i + 1, j + 3), (i + 2, j + 3), (i + 3, j + 3)]
199  mean_array = []
200  for point in point_array:
201  dqf = dqf_data_array[point[0]][point[1]]
202  if dqf == 0 or dqf == 1:
203  mean_array.append(rad_data_array[point[0]][point[1]])
204  if len(mean_array) != 0:
205  new_rad_data_array[k][n] = np.mean(mean_array)
206  new_dqf_data_array[k][n] = 0
207  else:
208  new_rad_data_array[k][n] = -999
209  new_dqf_data_array[k][n] = -1
210  n += 1
211  if n == new_dim:
212  n = 0
213  k += 1
214 
215  return new_rad_data_array, new_dqf_data_array
216 
217  @staticmethod
218  def _string_to_abimode(string):
219  """
220  Selects the ABI Mode Enum constant from string.
221  string - the string used for the match
222  """
223  if 'M4' in string:
224  return ABIMode.ABI_SCAN_MODE_4
225  if 'M6' in string:
226  return ABIMode.ABI_SCAN_MODE_6
227 
228  @staticmethod
230  """
231  Selects the ABI Sector Type constant from string.
232  string - the string used for the match
233  """
234  if 'F' in string:
235  return ABISectorType.FULL_DISK
236  if 'C' in string:
237  return ABISectorType.CONUS
238  if 'M1' in string:
239  return ABISectorType.MESOSCALE_REGION_1
240  if 'M2' in string:
241  return ABISectorType.MESOSCALE_REGION_2
242 
243  def _filter_data_array_by_yaw_flip_flag(self, data_array):
244  """
245  Returns data_array after filtering by the yaw_flip_flag.
246  data_array - the data array to filter
247  """
248  if not self._yaw_flip_flag_yaw_flip_flag:
249  return data_array[::-1]
250  else:
251  return data_array
252 
254  """
255  Creates a local data array variable containing the calculated obsvalue reflectance factor data
256  after fill value filtering by the DQF flags.
257  """
258  temp_data_array = self._rad_data_array_rad_data_array * self._kappa0_kappa0
259  self._obsvalue_rf_data_array_obsvalue_rf_data_array = np.where(self._dqf_data_array_dqf_data_array == -999, -999, temp_data_array)
260 
262  """
263  Creates a local data array variable containing the calculated obsvalue brightness temperature data
264  after fill value filtering by the DQF flags.
265  """
266  log_comp = np.log((self._planck_fk1_planck_fk1 / self._rad_data_array_rad_data_array) + 1)
267  temp_data_array = ((self._planck_fk2_planck_fk2 / log_comp) - self._planck_bc1_planck_bc1) / self._planck_bc2_planck_bc2
268  self._obsvalue_bt_data_array_obsvalue_bt_data_array = np.where(self._dqf_data_array_dqf_data_array == -999, -999, temp_data_array)
269 
271  """
272  Creates a local data array variable containing the calculated obserror reflectance factor data
273  after fill value filtering by the DQF flags.
274  """
275  sqrt_comp = np.power(self._kappa0_kappa0, 2) * np.power(self._std_dev_radiance_value_of_valid_pixels_std_dev_radiance_value_of_valid_pixels, 2)
276  temp_data_array = np.sqrt(sqrt_comp) / np.sqrt(self._valid_pixel_count_valid_pixel_count)
277  self._obserror_rf_data_array_obserror_rf_data_array = np.where(self._dqf_data_array_dqf_data_array == -999, -999, temp_data_array)
278 
280  """
281  Creates a local data array variable containing the calculated obserror brightness temperature data
282  after fill value filtering by the DQF flags.
283  """
284  sqrt_comp_1 = (-1.0 * self._planck_fk2_planck_fk2) / \
285  (self._planck_bc2_planck_bc2 * np.power(np.log((self._planck_fk1_planck_fk1 / self._rad_data_array_rad_data_array) + 1), 2))
286  sqrt_comp_2 = 1 / (self._planck_fk1_planck_fk1 + self._rad_data_array_rad_data_array) - 1 / self._rad_data_array_rad_data_array
287  sqrt_comp = np.power(sqrt_comp_1 * sqrt_comp_2, 2) * np.power(self._std_dev_radiance_value_of_valid_pixels_std_dev_radiance_value_of_valid_pixels, 2)
288  temp_data_array = np.sqrt(sqrt_comp) / np.sqrt(self._valid_pixel_count_valid_pixel_count)
289  self._obserror_bt_data_array_obserror_bt_data_array = np.where(self._dqf_data_array_dqf_data_array == -999, -999, temp_data_array)
290 
291  def get_abi_channel(self):
292  """
293  Returns the ABI channel.
294  """
295  return self._metadata_dict_metadata_dict['abi_channel']
296 
298  """
299  Returns the platform identifier.
300  """
301  return self._metadata_dict_metadata_dict['platform_identifier']
302 
303  def get_start_date(self):
304  """
305  Returns the scan's start date.
306  """
307  return self._metadata_dict_metadata_dict['start_date']
308 
310  """
311  Returns the input_file_path.
312  """
313  return self._input_file_path_input_file_path
314 
316  """
317  Returns the obsvalue reflectance factor data array.
318  """
319  return self._obsvalue_rf_data_array_obsvalue_rf_data_array
320 
322  """
323  Returns the obsvalue brightness temperature data array.
324  """
325  return self._obsvalue_bt_data_array_obsvalue_bt_data_array
326 
328  """
329  Returns the obserror reflectance factor data array.
330  """
331  return self._obserror_rf_data_array_obserror_rf_data_array
332 
334  """
335  Returns the obserror brightness temperature data array.
336  """
337  return self._obserror_bt_data_array_obserror_bt_data_array
338 
340  """
341  Returns the preqc data array.
342  """
343  return self._dqf_data_array_dqf_data_array
344 
345  def close(self):
346  """
347  Closes this netCDF4 Dataset.
348  """
349  self._input_dataset_input_dataset.close()
350 
351  def load(self):
352  """
353  Loads, calculates, subsamples, reshapes, and filters all data arrays required by the GoesConverter class.
354  """
355  self._open_open()
356  self._input_dataset_input_dataset.set_auto_scale(True)
357  self._load_yaw_flip_flag_variable_load_yaw_flip_flag_variable()
358  self._load_kappa0_variable_load_kappa0_variable()
359  self._load_planck_variables_load_planck_variables()
360  self._load_std_dev_radiance_value_of_valid_pixels_variable_load_std_dev_radiance_value_of_valid_pixels_variable()
361  self._load_valid_pixel_count_variable_load_valid_pixel_count_variable()
362  self._load_dqf_data_array_load_dqf_data_array()
363  self._load_rad_data_array_load_rad_data_array()
364 
365  if self._metadata_dict_metadata_dict['abi_channel'] == 1 or \
366  self._metadata_dict_metadata_dict['abi_channel'] == 3 or \
367  self._metadata_dict_metadata_dict['abi_channel'] == 5:
368  self._rad_data_array_rad_data_array, self._dqf_data_array_dqf_data_array = Goes._subsample(self._rad_data_array_rad_data_array,
369  self._dqf_data_array_dqf_data_array, 2)
370  if self._metadata_dict_metadata_dict['abi_channel'] == 2:
371  self._rad_data_array_rad_data_array, self._dqf_data_array_dqf_data_array = Goes._subsample(self._rad_data_array_rad_data_array,
372  self._dqf_data_array_dqf_data_array, 4)
373 
374  shape = len(self._rad_data_array_rad_data_array) * len(self._rad_data_array_rad_data_array)
375 
376  self._dqf_data_array_dqf_data_array = np.array(self._dqf_data_array_dqf_data_array)
377  self._dqf_data_array_dqf_data_array = self._dqf_data_array_dqf_data_array.reshape(shape)
378  self._dqf_data_array_dqf_data_array = self._filter_data_array_by_yaw_flip_flag_filter_data_array_by_yaw_flip_flag(self._dqf_data_array_dqf_data_array)
379  self._dqf_data_array_dqf_data_array = np.where(self._dqf_data_array_dqf_data_array == 255, -999, self._dqf_data_array_dqf_data_array)
380 
381  self._rad_data_array_rad_data_array = np.array(self._rad_data_array_rad_data_array)
382  self._rad_data_array_rad_data_array = self._rad_data_array_rad_data_array.reshape(shape)
383  self._rad_data_array_rad_data_array = self._filter_data_array_by_yaw_flip_flag_filter_data_array_by_yaw_flip_flag(self._rad_data_array_rad_data_array)
384  self._rad_data_array_rad_data_array = np.where(self._dqf_data_array_dqf_data_array == -999, -999, self._rad_data_array_rad_data_array)
385 
386  if self._metadata_dict_metadata_dict['abi_channel'] < 7:
387  self._create_obsvalue_rf_data_array_create_obsvalue_rf_data_array()
388  self._create_obserror_rf_data_array_create_obserror_rf_data_array()
389  else:
390  self._create_obsvalue_bt_data_array_create_obsvalue_bt_data_array()
391  self._create_obserror_bt_data_array_create_obserror_bt_data_array()
392 
393  self.closeclose()
394 
395 
396 #
397 # This enumeration is for the ABI Mode.
398 #
399 class ABIMode(Enum):
400  ABI_SCAN_MODE_4 = 1
401  ABI_SCAN_MODE_6 = 2
402 
403 
404 #
405 # This enumeration is for the ABI Sector Type
406 #
407 class ABISectorType(Enum):
408  FULL_DISK = 1
409  CONUS = 2
410  MESOSCALE_REGION_1 = 3
411  MESOSCALE_REGION_2 = 4
_planck_bc1
Definition: goes.py:79
_planck_fk2
Definition: goes.py:82
def _load_std_dev_radiance_value_of_valid_pixels_variable(self)
Definition: goes.py:84
def _open(self)
Definition: goes.py:57
def _create_obserror_bt_data_array(self)
Definition: goes.py:279
_obsvalue_rf_data_array
Definition: goes.py:35
def get_platform_identifier(self)
Definition: goes.py:297
def _load_rad_data_array(self)
Definition: goes.py:103
def _load_yaw_flip_flag_variable(self)
Definition: goes.py:63
_rad_data_array
Definition: goes.py:33
_planck_bc2
Definition: goes.py:80
def get_input_file_path(self)
Definition: goes.py:309
def _load_kappa0_variable(self)
Definition: goes.py:69
def _get_metadata_from_input_file_path(self)
Definition: goes.py:40
def close(self)
Definition: goes.py:345
def _string_to_abimode(string)
Definition: goes.py:218
_std_dev_radiance_value_of_valid_pixels
Definition: goes.py:88
def _downscale_05km_to_2km(rad_data_array, dqf_data_array)
Definition: goes.py:181
_obserror_bt_data_array
Definition: goes.py:38
def _subsample2(rad_data_array, dqf_data_array, increment)
Definition: goes.py:123
_valid_pixel_count
Definition: goes.py:95
_kappa0
Definition: goes.py:73
def get_abi_channel(self)
Definition: goes.py:291
def load(self)
Definition: goes.py:351
def get_obserror_bt_data_array(self)
Definition: goes.py:333
def _downscale_1km_to_2km(rad_data_array, dqf_data_array)
Definition: goes.py:146
def _filter_data_array_by_yaw_flip_flag(self, data_array)
Definition: goes.py:243
_obsvalue_bt_data_array
Definition: goes.py:36
def _create_obserror_rf_data_array(self)
Definition: goes.py:270
_obserror_rf_data_array
Definition: goes.py:37
_dqf_data_array
Definition: goes.py:34
def _string_to_abisectortype(string)
Definition: goes.py:229
def _load_valid_pixel_count_variable(self)
Definition: goes.py:91
def _load_dqf_data_array(self)
Definition: goes.py:97
def _load_planck_variables(self)
Definition: goes.py:75
def _subsample(rad_data_array, dqf_data_array, increment)
Definition: goes.py:110
_metadata_dict
Definition: goes.py:44
_yaw_flip_flag
Definition: goes.py:67
def get_obsvalue_rf_data_array(self)
Definition: goes.py:315
_input_dataset
Definition: goes.py:61
def _create_obsvalue_rf_data_array(self)
Definition: goes.py:253
def get_obsvalue_bt_data_array(self)
Definition: goes.py:321
def _create_obsvalue_bt_data_array(self)
Definition: goes.py:261
def get_preqc_data_array(self)
Definition: goes.py:339
def __init__(self, input_file_path)
Definition: goes.py:26
_planck_fk1
Definition: goes.py:81
def get_start_date(self)
Definition: goes.py:303
def get_obserror_rf_data_array(self)
Definition: goes.py:327
_input_file_path
Definition: goes.py:31