11import os
2+ import subprocess
23
34import cartopy .crs as ccrs
45import cartopy .feature as cfeature
56import matplotlib .pyplot as plt
67import netCDF4
78import numpy as np
89from matplotlib import colormaps
9- from mpas_tools .logging import check_call
1010
1111from compass .step import Step
1212
@@ -42,7 +42,8 @@ def __init__(self, test_case):
4242
4343 self .harmonic_analysis_file = 'harmonicAnalysis.nc'
4444 self .grid_file = 'initial_state.nc'
45- self .constituents = ['k1' , 'k2' , 'm2' , 'n2' , 'o1' , 'p1' , 'q1' , 's2' ]
45+ self .constituents_valid = ['k1' , 'k2' , 'm2' , 'n2' ,
46+ 'o1' , 'p1' , 'q1' , 's2' ]
4647
4748 self .add_input_file (
4849 filename = self .harmonic_analysis_file ,
@@ -60,6 +61,8 @@ def setup(self):
6061 config = self .config
6162 self .tpxo_version = config .get ('tides' , 'tpxo_version' )
6263
64+ self .constituents = config .getlist ('tides' , 'constituents' )
65+
6366 os .makedirs (f'{ self .work_dir } /TPXO_data' , exist_ok = True )
6467 if self .tpxo_version == 'TPXO9' :
6568 for constituent in self .constituents :
@@ -90,72 +93,79 @@ def setup(self):
9093 target = 'TPXO8/grid_tpxo8_atlas_30_v1' ,
9194 database = 'tides' )
9295
93- def write_coordinate_file (self , idx ):
96+ def write_coordinate_file (self , nchunks ):
9497 """
9598 Write mesh coordinates for TPXO extraction
9699 """
97100
98101 # Read in mesh
99102 grid_nc = netCDF4 .Dataset (self .grid_file , 'r' )
100- lon_grid = np .degrees (grid_nc .variables ['lonCell' ][idx ])
101- lat_grid = np .degrees (grid_nc .variables ['latCell' ][idx ])
102- nCells = len (lon_grid )
103+ lon_grid = np .degrees (grid_nc .variables ['lonCell' ][:])
104+ lat_grid = np .degrees (grid_nc .variables ['latCell' ][:])
105+
106+ lon_grid_split = np .array_split (lon_grid , nchunks )
107+ lat_grid_split = np .array_split (lat_grid , nchunks )
103108
104109 # Write coordinate file for OTPS2
105- f = open ('lat_lon' , 'w' )
106- for i in range (nCells ):
107- f .write (f'{ lat_grid [i ]} { lon_grid [i ]} \n ' )
108- f .close ()
110+ for chunk in range (nchunks ):
111+ f = open (f'inputs/lat_lon_{ chunk } ' , 'w' )
112+ for i in range (lon_grid_split [chunk ].size ):
113+ f .write (f'{ lat_grid_split [chunk ][i ]} '
114+ f'{ lon_grid_split [chunk ][i ]} \n ' )
115+ f .close ()
109116
110- def setup_otps2 (self ):
117+ def setup_otps2 (self , nchunks ):
111118 """
112119 Write input files for TPXO extraction
113120 """
114121
115122 for con in self .constituents :
116123 print (f'setup { con } ' )
117124
118- # Lines for the setup_con files
119- lines = [{'inp' : f'inputs/Model_atlas_{ con } ' ,
120- 'comment' : '! 1. tidal model control file' },
121- {'inp' : 'lat_lon' ,
122- 'comment' : '! 2. latitude/longitude/<time> file' },
123- {'inp' : 'z' ,
124- 'comment' : '! 3. z/U/V/u/v' },
125- {'inp' : con ,
126- 'comment' : '! 4. tidal constituents to include' },
127- {'inp' : 'AP' ,
128- 'comment' : '! 5. AP/RI' },
129- {'inp' : 'oce' ,
130- 'comment' : '! 6. oce/geo' },
131- {'inp' : '1' ,
132- 'comment' : '! 7. 1/0 correct for minor constituents' },
133- {'inp' : f'outputs/{ con } .out' ,
134- 'comment' : '! 8. output file (ASCII)' }]
135-
136- # Create directory for setup_con and Model_atlas_con files
137- if not os .path .exists ('inputs' ):
138- os .mkdir ('inputs' )
139-
140- # Write the setup_con file
141- f = open (f'inputs/{ con } _setup' , 'w' )
142- for line in lines :
143- spaces = 28 - len (line ['inp' ])
144- f .write (line ['inp' ] + spaces * ' ' + line ['comment' ] + '\n ' )
145- f .close ()
146-
147- # Write the Model_atlas_con file
148- f = open (f'inputs/Model_atlas_{ con } ' , 'w' )
149- f .write (f'TPXO_data/h_{ con } _tpxo\n ' )
150- f .write (f'TPXO_data/u_{ con } _tpxo\n ' )
151- f .write ('TPXO_data/grid_tpxo' )
152- f .close ()
153-
154- # Create directory for the con.out files
155- if not os .path .exists ('outputs' ):
156- os .mkdir ('outputs' )
157-
158- def run_otps2 (self ):
125+ for chunk in range (nchunks ):
126+
127+ # Lines for the setup_con files
128+ lines = [{'inp' : f'inputs/Model_atlas_{ con } ' ,
129+ 'comment' : '! 1. tidal model control file' },
130+ {'inp' : f'inputs/lat_lon_{ chunk } ' ,
131+ 'comment' : '! 2. latitude/longitude/<time> file' },
132+ {'inp' : 'z' ,
133+ 'comment' : '! 3. z/U/V/u/v' },
134+ {'inp' : con ,
135+ 'comment' : '! 4. tidal constituents to include' },
136+ {'inp' : 'AP' ,
137+ 'comment' : '! 5. AP/RI' },
138+ {'inp' : 'oce' ,
139+ 'comment' : '! 6. oce/geo' },
140+ {'inp' : '1' ,
141+ 'comment' :
142+ '! 7. 1/0 correct for minor constituents' },
143+ {'inp' : f'outputs/{ con } _{ chunk } .out' ,
144+ 'comment' : '! 8. output file (ASCII)' }]
145+
146+ # Create directory for setup_con and Model_atlas_con files
147+ if not os .path .exists ('inputs' ):
148+ os .mkdir ('inputs' )
149+
150+ # Write the setup_con file
151+ f = open (f'inputs/{ con } _setup_{ chunk } ' , 'w' )
152+ for line in lines :
153+ spaces = (28 - len (line ['inp' ])) * ' '
154+ f .write (f"{ line ['inp' ]} { spaces } { line ['comment' ]} \n " )
155+ f .close ()
156+
157+ # Write the Model_atlas_con file
158+ f = open (f'inputs/Model_atlas_{ con } ' , 'w' )
159+ f .write (f'TPXO_data/h_{ con } _tpxo\n ' )
160+ f .write (f'TPXO_data/u_{ con } _tpxo\n ' )
161+ f .write ('TPXO_data/grid_tpxo' )
162+ f .close ()
163+
164+ # Create directory for the con.out files
165+ if not os .path .exists ('outputs' ):
166+ os .mkdir ('outputs' )
167+
168+ def run_otps2 (self , nchunks ):
159169 """
160170 Perform TPXO extraction
161171 """
@@ -164,35 +174,46 @@ def run_otps2(self):
164174 for con in self .constituents :
165175 print ('' )
166176 print (f'run { con } ' )
167- check_call (f'extract_HC < inputs/{ con } _setup' ,
168- logger = self .logger , shell = True )
169177
170- def read_otps2_output (self , idx ):
178+ commands = []
179+ for chunk in range (nchunks ):
180+ commands .append (f'extract_HC < inputs/{ con } _setup_{ chunk } ' )
181+
182+ processes = [subprocess .Popen (cmd , shell = True ) for cmd in commands ]
183+
184+ for p in processes :
185+ p .wait ()
186+
187+ def read_otps2_output (self , nchunks ):
171188 """
172189 Read TPXO extraction output
173190 """
174191
175- start = idx [0 ]
176192 for con in self .constituents :
177193
178- f = open (f'outputs/{ con } .out' , 'r' )
179- lines = f .read ().splitlines ()
180- for i , line in enumerate (lines [3 :]):
181- line_sp = line .split ()
182- if line_sp [2 ] != '*************' :
183- val = float (line_sp [2 ])
184- self .mesh_AP [con ]['amp' ][start + i ] = val
185- else :
186- self .mesh_AP [con ]['amp' ][start + i ] = - 9999
187-
188- if line_sp [3 ] != 'Site' :
189- val = float (line_sp [3 ])
190- if val < 0 :
191- val = val + 360.0
192- self .mesh_AP [con ]['phase' ][start + i ] = val
193-
194- else :
195- self .mesh_AP [con ]['phase' ][start + i ] = - 9999
194+ i = 0
195+ for chunk in range (nchunks ):
196+
197+ f = open (f'outputs/{ con } _{ chunk } .out' , 'r' )
198+ lines = f .read ().splitlines ()
199+ for line in lines [3 :]:
200+ line_sp = line .split ()
201+ if line_sp [2 ] != '*************' :
202+ val = float (line_sp [2 ])
203+ self .mesh_AP [con ]['amp' ][i ] = val
204+ else :
205+ self .mesh_AP [con ]['amp' ][i ] = - 9999
206+
207+ if line_sp [3 ] != 'Site' :
208+ val = float (line_sp [3 ])
209+ if val < 0 :
210+ val = val + 360.0
211+ self .mesh_AP [con ]['phase' ][i ] = val
212+
213+ else :
214+ self .mesh_AP [con ]['phase' ][i ] = - 9999
215+
216+ i = i + 1
196217
197218 def append_tpxo_data (self ):
198219 """
@@ -283,7 +304,7 @@ def plot(self):
283304 depth [:] = data_nc .variables ['bottomDepth' ][:]
284305 area [:] = data_nc .variables ['areaCell' ][:]
285306
286- constituent_list = ['K1' , 'M2' , 'N2' , 'O1' , 'S2' ]
307+ constituent_list = [x . upper () for x in self . constituents ]
287308
288309 # Use these to fix up the plots
289310 subplot_ticks = [[np .linspace (0 , 0.65 , 10 ), np .linspace (0 , 0.65 , 10 ),
@@ -429,16 +450,16 @@ def run(self):
429450 Run this step of the test case
430451 """
431452
453+ self .constituents = self .config .getlist ('tides' , 'constituents' )
454+
432455 # Check if TPXO values aleady exist in harmonic_analysis.nc
433456 self .check_tpxo_data ()
434457
435- # Setup input files for TPXO extraction
436- self .setup_otps2 ()
437-
438458 # Setup chunking for TPXO extraction with large meshes
439- indices = np .arange (self .nCells )
440- nchunks = np .ceil (self .nCells / 200000 )
441- index_chunks = np .array_split (indices , nchunks )
459+ nchunks = 128
460+
461+ # Setup input files for TPXO extraction
462+ self .setup_otps2 (nchunks )
442463
443464 # Initialize data structure for TPXO values
444465 self .mesh_AP = {}
@@ -447,10 +468,9 @@ def run(self):
447468 'phase' : np .zeros ((self .nCells ))}
448469
449470 # Extract TPXO values
450- for idx in index_chunks :
451- self .write_coordinate_file (idx )
452- self .run_otps2 ()
453- self .read_otps2_output (idx )
471+ self .write_coordinate_file (nchunks )
472+ self .run_otps2 (nchunks )
473+ self .read_otps2_output (nchunks )
454474
455475 # Inject TPXO values
456476 self .append_tpxo_data ()
0 commit comments