6
6
import matplotlib .pyplot as plt
7
7
import numpy as np
8
8
from mpas_tools .cime .constants import constants
9
+ from mpas_tools .mesh .cull import write_map_culled_to_base
9
10
from netCDF4 import Dataset
10
11
from scipy import interpolate , spatial
11
12
@@ -16,21 +17,44 @@ class WavesBaseMesh(QuasiUniformSphericalMeshStep):
16
17
"""
17
18
A step for creating wave mesh based on an ocean mesh
18
19
"""
19
- def __init__ (self , test_case , ocean_mesh , name = 'base_mesh' , subdir = None ):
20
+ def __init__ (self , test_case , ocean_base_mesh , ocean_culled_mesh ,
21
+ name = 'base_mesh' , subdir = None ):
20
22
21
23
super ().__init__ (test_case = test_case , name = name , subdir = subdir ,
22
24
cell_width = None )
23
25
24
- mesh_path = ocean_mesh .steps ['initial_state' ].path
25
- self .add_input_file (
26
- filename = 'ocean_mesh.nc' ,
27
- work_dir_target = f'{ mesh_path } /initial_state.nc' )
26
+ self .ocean_base_mesh = ocean_base_mesh
27
+ self .ocean_culled_mesh = ocean_culled_mesh
28
28
29
29
def setup (self ):
30
30
31
31
super ().setup ()
32
32
self .opts .init_file = 'init.msh'
33
33
34
+ # Set links to base mesh
35
+ if self .config .has_option ('wave_mesh' , 'ocean_base_mesh' ):
36
+ ocean_base_mesh_path = self .config .get ('wave_mesh' ,
37
+ 'ocean_base_mesh' )
38
+ else :
39
+ mesh_path = self .ocean_base_mesh .steps ['base_mesh' ].path
40
+ ocean_base_mesh_path = f'{ mesh_path } /base_mesh.nc'
41
+
42
+ self .add_input_file (
43
+ filename = 'ocean_base_mesh.nc' ,
44
+ work_dir_target = ocean_base_mesh_path )
45
+
46
+ # Set links to culled mesh
47
+ if self .config .has_option ('wave_mesh' , 'ocean_culled_mesh' ):
48
+ ocean_culled_mesh_path = self .config .get ('wave_mesh' ,
49
+ 'ocean_culled_mesh' )
50
+ else :
51
+ mesh_path = self .ocean_culled_mesh .steps ['initial_state' ].path
52
+ ocean_culled_mesh_path = f'{ mesh_path } /initial_state.nc'
53
+
54
+ self .add_input_file (
55
+ filename = 'ocean_culled_mesh.nc' ,
56
+ work_dir_target = ocean_culled_mesh_path )
57
+
34
58
def build_cell_width_lat_lon (self ):
35
59
"""
36
60
Create cell width array for this mesh on a regular latitude-longitude
@@ -62,10 +86,13 @@ def build_cell_width_lat_lon(self):
62
86
63
87
earth_radius = constants ['SHR_CONST_REARTH' ] / km
64
88
cell_width = self .cell_widthVsLatLon (xlon , ylat ,
65
- earth_radius , 'ocean_mesh.nc' )
89
+ earth_radius ,
90
+ 'ocean_culled_mesh.nc' )
66
91
cell_width = cell_width / km
67
92
68
- self .create_initial_points ('ocean_mesh.nc' , xlon , ylat , cell_width ,
93
+ self .create_initial_points ('ocean_base_mesh.nc' ,
94
+ 'ocean_culled_mesh.nc' ,
95
+ xlon , ylat , cell_width ,
69
96
earth_radius , self .opts .init_file )
70
97
71
98
hfun_slope_lim = self .config .getfloat ('wave_mesh' , 'hfun_slope_lim' )
@@ -248,24 +275,29 @@ def distance_to_shapefile_points(self, lon, lat,
248
275
249
276
return D
250
277
251
- def create_initial_points (self , meshfile , lon , lat , hfunction ,
278
+ def create_initial_points (self , base_mesh , culled_mesh ,
279
+ lon , lat , hfunction ,
252
280
sphere_radius , outfile ):
253
281
254
- # Open MPAS mesh and get cell variables
255
- nc_file = Dataset (meshfile , 'r' )
256
- lonCell = nc_file .variables ['lonCell' ][:]
257
- latCell = nc_file .variables ['latCell' ][:]
258
- nCells = lonCell .shape [0 ]
282
+ # Open MPAS culled mesh and get cell variables
283
+ nc_file_culled = Dataset (culled_mesh , 'r' )
284
+ lonCell_culled = nc_file_culled .variables ['lonCell' ][:]
285
+ nCells_culled = lonCell_culled .shape [0 ]
286
+
287
+ # Open MPAS base mesh and get cell variables
288
+ nc_file_base = Dataset (base_mesh , 'r' )
289
+ lonCell_base = nc_file_base .variables ['lonCell' ][:]
290
+ latCell_base = nc_file_base .variables ['latCell' ][:]
259
291
260
292
# Transform 0,360 range to -180,180
261
- idx , = np .where (lonCell > np .pi )
262
- lonCell [idx ] = lonCell [idx ] - 2.0 * np .pi
293
+ idx , = np .where (lonCell_base > np .pi )
294
+ lonCell_base [idx ] = lonCell_base [idx ] - 2.0 * np .pi
263
295
264
296
# Interpolate hfunction onto mesh cell centers
265
297
hfun = interpolate .RegularGridInterpolator (
266
298
(np .radians (lon ), np .radians (lat )),
267
299
hfunction .T )
268
- mesh_pts = np .vstack ((lonCell , latCell )).T
300
+ mesh_pts = np .vstack ((lonCell_base , latCell_base )).T
269
301
hfun_interp = hfun (mesh_pts )
270
302
271
303
# Find cells in refined region of waves mesh
@@ -277,34 +309,47 @@ def create_initial_points(self, meshfile, lon, lat, hfunction,
277
309
# in the extra columns of cellsOnCell
278
310
# in this case, these must be zeroed out
279
311
# to correctly identify boundary cells
280
- nEdgesOnCell = nc_file .variables ['nEdgesOnCell' ][:]
281
- cellsOnCell = nc_file .variables ['cellsOnCell' ][:]
282
- nz = np .zeros (cellsOnCell .shape , dtype = bool )
283
- for i in range (nCells ):
284
- nz [i , 0 :nEdgesOnCell [i ]] = True
285
- cellsOnCell [~ nz ] = 0
286
- nCellsOnCell = np .count_nonzero (cellsOnCell , axis = 1 )
287
- is_boundary_cell = np .equal (nCellsOnCell , nEdgesOnCell )
312
+ nEdgesOnCell_culled = nc_file_culled .variables ['nEdgesOnCell' ][:]
313
+ cellsOnCell_culled = nc_file_culled .variables ['cellsOnCell' ][:]
314
+ nz = np .zeros (cellsOnCell_culled .shape , dtype = bool )
315
+ for i in range (nCells_culled ):
316
+ nz [i , 0 :nEdgesOnCell_culled [i ]] = True
317
+ cellsOnCell_culled [~ nz ] = 0
318
+ nCellsOnCell = np .count_nonzero (cellsOnCell_culled , axis = 1 )
319
+ is_boundary_cell = np .equal (nCellsOnCell , nEdgesOnCell_culled )
288
320
idx_bnd , = np .where (is_boundary_cell == False ) # noqa: E712
289
321
290
- # Force inclusion of all boundary cells
291
- idx = np .union1d (idx , idx_bnd )
322
+ # Map from culled mesh cells to base mesh cells
323
+ write_map_culled_to_base (base_mesh , culled_mesh ,
324
+ 'base_to_culled_map.nc' )
325
+ map_file = Dataset ('base_to_culled_map.nc' , 'r' )
326
+ culled_to_base = map_file .variables ['mapCulledToBaseCell' ][:]
327
+ base_idx_bnd = culled_to_base [idx_bnd ]
328
+
329
+ # Find cells in base mesh connected to culled mesh boundary cells
330
+ cellsOnCell_base = nc_file_base .variables ['cellsOnCell' ][:]
331
+ base_idx_xbnd = np .concatenate (cellsOnCell_base [base_idx_bnd ])
332
+ base_idx_xbnd = base_idx_xbnd - 1
333
+
334
+ # Force inclusion of all boundary cells, eliminating any duplicates
335
+ idx = np .union1d (idx , base_idx_bnd )
336
+ idx = np .union1d (idx , base_idx_xbnd )
292
337
293
338
# Get coordinates of points
294
- lon = lonCell [idx ]
295
- lat = latCell [idx ]
339
+ lon = lonCell_base [idx ]
340
+ lat = latCell_base [idx ]
296
341
342
+ # Include north pole point
297
343
lon = np .append (lon , 0.0 )
298
344
lat = np .append (lat , 0.5 * np .pi )
299
345
300
- npt = lon .size
301
-
302
346
# Change to Cartesian coordinates
303
347
x , y , z = self .lonlat2xyz (lon , lat , sphere_radius )
304
348
305
349
# Get coordinates and ID into structured array
306
350
# (for use with np.savetxt)
307
351
pt_list = []
352
+ npt = lon .size
308
353
for i in range (npt ):
309
354
# ID of -1 specifies that node is fixed
310
355
pt_list .append ((x [i ], y [i ], z [i ], - 1 ))
0 commit comments