IODA Bundle
run_satwnds.py
Go to the documentation of this file.
1 #!/usr/bin/env python3
2 #
3 # (C) Copyright 2020 NOAA/NWS/NCEP/EMC
4 #
5 # This software is licensed under the terms of the Apache Licence Version 2.0
6 # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
7 #
8 
9 import argparse
10 from datetime import datetime
11 import glob
12 import multiprocessing
13 import os
14 import pathlib
15 import re
16 import shutil
17 import subprocess
18 
19 
20 # Regex patterns that identify subset categories
21 OLD_SUBSET_PATTERN = r'NC0050([1245678]\d|90)'
22 NEW_SUBSET_PATTERN = r'NC0050(3\d|91)'
23 
24 # Define template file paths
25 EXE_DIR = pathlib.Path(__file__).parent.absolute()
26 OLD_SUBSET_TEMPLATE_PATH = os.path.join(EXE_DIR,
27  'satwnds_old_subset_template.yaml')
28 NEW_SUBSET_TEMPLATE_PATH = os.path.join(EXE_DIR,
29  'satwnds_new_subset_template.yaml')
30 
31 
32 def _make_file_from_template(template_path, replacements, output_path):
33  tag_re = re.compile(r'{{\s*(?P<key>\w+)\s*}}')
34 
35  lines = []
36  with open(template_path, 'r') as template_file:
37  for line in template_file.readlines():
38  matches = tag_re.findall(line)
39  for match_key in matches:
40  if match_key in replacements:
41  line = tag_re.sub(replacements[match_key], line)
42  else:
43  raise Exception(f'Unknown tag with key {match_key} in \
44  {template_path}')
45  lines.append(line)
46 
47  with open(output_path, 'w') as new_file:
48  new_file.writelines(lines)
49 
50 
52  file_name = os.path.split(path)[1]
53  print(f'Running {file_name}.')
54 
55  if re.match(OLD_SUBSET_PATTERN, file_name):
56  yaml_template = OLD_SUBSET_TEMPLATE_PATH
57  elif re.match(NEW_SUBSET_PATTERN, file_name):
58  yaml_template = NEW_SUBSET_TEMPLATE_PATH
59  else:
60  print(f'Warning: Found undefined subset {file_name}')
61  return
62 
63  yaml_out_path = f'{file_name}.yaml'
64  _make_file_from_template(yaml_template,
65  {'obsdatain': file_name,
66  'obsdataout': f'{file_name}.nc'},
67  yaml_out_path)
68 
69  subprocess.call(f'bufr2ioda.x {yaml_out_path}', shell=True)
70 
71  # Cleanup
72  os.remove(yaml_out_path)
73 
74 
75 def run(bufr_path, num_threads, output_dir):
76  """
77  Splits a Sat Winds file into its subset components and runs bufr2ioda on
78  each one.
79  :param bufr_path: Path to the Sat winds Bufr file.
80  :param num_threads: Number of concurrent converters.
81  :param output_dir: Directory were to place the result files
82  """
83 
84  def _set_up_working_dir(out_dir):
85  timestamp_str = datetime.now().strftime("%Y%m%d%H%M%S")
86 
87  if out_dir:
88  working_dir = out_dir
89  else:
90  working_dir = f'satwnd_processing_{timestamp_str}'
91 
92  if os.path.exists(working_dir):
93  shutil.rmtree(working_dir)
94 
95  os.mkdir(working_dir)
96  os.chdir(working_dir)
97 
98  def _clean_working_dir(bufr_paths):
99  for path in bufr_paths:
100  os.remove(path)
101 
102  input_path = os.path.realpath(bufr_path)
103 
104  _set_up_working_dir(output_dir)
105 
106  # Split the input file
107  subprocess.call(f'split_by_subset.x {input_path}', shell=True)
108 
109  # Process each subset bufr file.
110  bufr_paths = glob.glob('NC*')
111  pool = multiprocessing.Pool(num_threads)
112  pool.map(_process_bufr_path, bufr_paths)
113 
114  # Cleanup
115  _clean_working_dir(bufr_paths)
116 
117 
118 if __name__ == '__main__':
119  DESCRIPTION = 'Split a sat wind bufr file into its subsets, and runs \
120  bufr2ioda.x on each one with the proper configuration.'
121 
122  parser = argparse.ArgumentParser(description=DESCRIPTION)
123  parser.add_argument('file',
124  type=str,
125  help="SatWnd file to split.")
126 
127  parser.add_argument('-t',
128  '--threads',
129  default=1,
130  type=int,
131  help="Number of concurrent instances of bufr2ioda.")
132 
133  parser.add_argument('-o',
134  '--output_dir',
135  default="",
136  type=str,
137  help="Directory where to put the resulting netcdf files.")
138 
139  args = parser.parse_args()
140 
141  start_time = datetime.now()
142  run(args.file, args.threads, args.output_dir)
143  print((datetime.now() - start_time).total_seconds())
def _make_file_from_template(template_path, replacements, output_path)
Definition: run_satwnds.py:32
def _process_bufr_path(path)
Definition: run_satwnds.py:51
def run(bufr_path, num_threads, output_dir)
Definition: run_satwnds.py:75