1
1
import os
2
+ import subprocess
2
3
3
4
import cartopy .crs as ccrs
4
5
import cartopy .feature as cfeature
5
6
import matplotlib .pyplot as plt
6
7
import netCDF4
7
8
import numpy as np
8
9
from matplotlib import colormaps
9
- from mpas_tools .logging import check_call
10
10
11
11
from compass .step import Step
12
12
@@ -42,7 +42,8 @@ def __init__(self, test_case):
42
42
43
43
self .harmonic_analysis_file = 'harmonicAnalysis.nc'
44
44
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' ]
46
47
47
48
self .add_input_file (
48
49
filename = self .harmonic_analysis_file ,
@@ -60,6 +61,8 @@ def setup(self):
60
61
config = self .config
61
62
self .tpxo_version = config .get ('tides' , 'tpxo_version' )
62
63
64
+ self .constituents = config .getlist ('tides' , 'constituents' )
65
+
63
66
os .makedirs (f'{ self .work_dir } /TPXO_data' , exist_ok = True )
64
67
if self .tpxo_version == 'TPXO9' :
65
68
for constituent in self .constituents :
@@ -90,72 +93,79 @@ def setup(self):
90
93
target = 'TPXO8/grid_tpxo8_atlas_30_v1' ,
91
94
database = 'tides' )
92
95
93
- def write_coordinate_file (self , idx ):
96
+ def write_coordinate_file (self , nchunks ):
94
97
"""
95
98
Write mesh coordinates for TPXO extraction
96
99
"""
97
100
98
101
# Read in mesh
99
102
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 )
103
108
104
109
# 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 ()
109
116
110
- def setup_otps2 (self ):
117
+ def setup_otps2 (self , nchunks ):
111
118
"""
112
119
Write input files for TPXO extraction
113
120
"""
114
121
115
122
for con in self .constituents :
116
123
print (f'setup { con } ' )
117
124
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 ):
159
169
"""
160
170
Perform TPXO extraction
161
171
"""
@@ -164,35 +174,46 @@ def run_otps2(self):
164
174
for con in self .constituents :
165
175
print ('' )
166
176
print (f'run { con } ' )
167
- check_call (f'extract_HC < inputs/{ con } _setup' ,
168
- logger = self .logger , shell = True )
169
177
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 ):
171
188
"""
172
189
Read TPXO extraction output
173
190
"""
174
191
175
- start = idx [0 ]
176
192
for con in self .constituents :
177
193
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
196
217
197
218
def append_tpxo_data (self ):
198
219
"""
@@ -283,7 +304,7 @@ def plot(self):
283
304
depth [:] = data_nc .variables ['bottomDepth' ][:]
284
305
area [:] = data_nc .variables ['areaCell' ][:]
285
306
286
- constituent_list = ['K1' , 'M2' , 'N2' , 'O1' , 'S2' ]
307
+ constituent_list = [x . upper () for x in self . constituents ]
287
308
288
309
# Use these to fix up the plots
289
310
subplot_ticks = [[np .linspace (0 , 0.65 , 10 ), np .linspace (0 , 0.65 , 10 ),
@@ -429,16 +450,16 @@ def run(self):
429
450
Run this step of the test case
430
451
"""
431
452
453
+ self .constituents = self .config .getlist ('tides' , 'constituents' )
454
+
432
455
# Check if TPXO values aleady exist in harmonic_analysis.nc
433
456
self .check_tpxo_data ()
434
457
435
- # Setup input files for TPXO extraction
436
- self .setup_otps2 ()
437
-
438
458
# 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 )
442
463
443
464
# Initialize data structure for TPXO values
444
465
self .mesh_AP = {}
@@ -447,10 +468,9 @@ def run(self):
447
468
'phase' : np .zeros ((self .nCells ))}
448
469
449
470
# 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 )
454
474
455
475
# Inject TPXO values
456
476
self .append_tpxo_data ()
0 commit comments