Skip to content

Commit 1550d55

Browse files
committed
Incorporate ocean base mesh into wave base mesh creation
- Also add the ability to (optionally) specify paths to existing base and culled ocean meshes in the config file
1 parent 62a4aa5 commit 1550d55

File tree

4 files changed

+101
-39
lines changed

4 files changed

+101
-39
lines changed

compass/ocean/tests/global_ocean/wave_mesh/__init__.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ def __init__(self, test_group, ocean_mesh, ocean_init):
4545
self.mesh_config_filename = 'wave_mesh.cfg'
4646

4747
base_mesh_step = WavesBaseMesh(test_case=self,
48-
ocean_mesh=ocean_init)
48+
ocean_base_mesh=ocean_mesh,
49+
ocean_culled_mesh=ocean_init)
4950
self.add_step(base_mesh_step)
5051

5152
cull_mesh_step = WavesCullMesh(test_case=self,

compass/ocean/tests/global_ocean/wave_mesh/base_mesh.py

+75-30
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import matplotlib.pyplot as plt
77
import numpy as np
88
from mpas_tools.cime.constants import constants
9+
from mpas_tools.mesh.cull import write_map_culled_to_base
910
from netCDF4 import Dataset
1011
from scipy import interpolate, spatial
1112

@@ -16,21 +17,44 @@ class WavesBaseMesh(QuasiUniformSphericalMeshStep):
1617
"""
1718
A step for creating wave mesh based on an ocean mesh
1819
"""
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):
2022

2123
super().__init__(test_case=test_case, name=name, subdir=subdir,
2224
cell_width=None)
2325

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
2828

2929
def setup(self):
3030

3131
super().setup()
3232
self.opts.init_file = 'init.msh'
3333

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+
3458
def build_cell_width_lat_lon(self):
3559
"""
3660
Create cell width array for this mesh on a regular latitude-longitude
@@ -62,10 +86,13 @@ def build_cell_width_lat_lon(self):
6286

6387
earth_radius = constants['SHR_CONST_REARTH'] / km
6488
cell_width = self.cell_widthVsLatLon(xlon, ylat,
65-
earth_radius, 'ocean_mesh.nc')
89+
earth_radius,
90+
'ocean_culled_mesh.nc')
6691
cell_width = cell_width / km
6792

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,
6996
earth_radius, self.opts.init_file)
7097

7198
hfun_slope_lim = self.config.getfloat('wave_mesh', 'hfun_slope_lim')
@@ -248,24 +275,29 @@ def distance_to_shapefile_points(self, lon, lat,
248275

249276
return D
250277

251-
def create_initial_points(self, meshfile, lon, lat, hfunction,
278+
def create_initial_points(self, base_mesh, culled_mesh,
279+
lon, lat, hfunction,
252280
sphere_radius, outfile):
253281

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'][:]
259291

260292
# 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
263295

264296
# Interpolate hfunction onto mesh cell centers
265297
hfun = interpolate.RegularGridInterpolator(
266298
(np.radians(lon), np.radians(lat)),
267299
hfunction.T)
268-
mesh_pts = np.vstack((lonCell, latCell)).T
300+
mesh_pts = np.vstack((lonCell_base, latCell_base)).T
269301
hfun_interp = hfun(mesh_pts)
270302

271303
# Find cells in refined region of waves mesh
@@ -277,34 +309,47 @@ def create_initial_points(self, meshfile, lon, lat, hfunction,
277309
# in the extra columns of cellsOnCell
278310
# in this case, these must be zeroed out
279311
# 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)
288320
idx_bnd, = np.where(is_boundary_cell == False) # noqa: E712
289321

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)
292337

293338
# Get coordinates of points
294-
lon = lonCell[idx]
295-
lat = latCell[idx]
339+
lon = lonCell_base[idx]
340+
lat = latCell_base[idx]
296341

342+
# Include north pole point
297343
lon = np.append(lon, 0.0)
298344
lat = np.append(lat, 0.5 * np.pi)
299345

300-
npt = lon.size
301-
302346
# Change to Cartesian coordinates
303347
x, y, z = self.lonlat2xyz(lon, lat, sphere_radius)
304348

305349
# Get coordinates and ID into structured array
306350
# (for use with np.savetxt)
307351
pt_list = []
352+
npt = lon.size
308353
for i in range(npt):
309354
# ID of -1 specifies that node is fixed
310355
pt_list.append((x[i], y[i], z[i], -1))

compass/ocean/tests/global_ocean/wave_mesh/cull_mesh.py

+12-4
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,7 @@ def __init__(self, test_case, ocean_mesh, wave_base_mesh,
1515

1616
super().__init__(test_case=test_case, name=name, subdir=subdir)
1717

18-
culled_mesh_path = ocean_mesh.steps['initial_state'].path
19-
self.add_input_file(
20-
filename='ocean_culled_mesh.nc',
21-
work_dir_target=f'{culled_mesh_path}/initial_state.nc')
18+
self.ocean_mesh = ocean_mesh
2219

2320
wave_base_mesh_path = wave_base_mesh.path
2421
self.add_input_file(
@@ -29,6 +26,17 @@ def setup(self):
2926

3027
super().setup()
3128

29+
if self.config.has_option('wave_mesh', 'ocean_culled_mesh'):
30+
culled_mesh_path = self.config.get('wave_mesh',
31+
'ocean_culled_mesh')
32+
else:
33+
mesh_path = self.ocean_mesh.steps['initial_state'].path
34+
culled_mesh_path = f'{mesh_path}/initial_state.nc'
35+
36+
self.add_input_file(
37+
filename='ocean_culled_mesh.nc',
38+
work_dir_target=culled_mesh_path)
39+
3240
template = Template(read_text(
3341
'compass.ocean.tests.global_ocean.wave_mesh',
3442
'cull_mesh.template'))

compass/ocean/tests/global_ocean/wave_mesh/scrip_file.py

+12-4
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,7 @@ def __init__(self, test_case, wave_culled_mesh, ocean_mesh,
1616

1717
super().__init__(test_case=test_case, name=name, subdir=subdir)
1818

19-
ocean_mesh_path = ocean_mesh.steps['initial_state'].path
20-
self.add_input_file(
21-
filename='ocean_mesh.nc',
22-
work_dir_target=f'{ocean_mesh_path}/initial_state.nc')
19+
self.ocean_mesh = ocean_mesh
2320

2421
wave_culled_mesh_path = wave_culled_mesh.path
2522
self.add_input_file(
@@ -30,6 +27,17 @@ def setup(self):
3027

3128
super().setup()
3229

30+
if self.config.has_option('wave_mesh', 'ocean_culled_mesh'):
31+
ocean_mesh_path = self.config.get('wave_mesh',
32+
'ocean_culled_mesh')
33+
else:
34+
mesh_path = self.ocean_mesh.steps['initial_state'].path
35+
ocean_mesh_path = f'{mesh_path}/initial_state.nc'
36+
37+
self.add_input_file(
38+
filename='ocean_mesh.nc',
39+
work_dir_target=ocean_mesh_path)
40+
3341
template = Template(read_text(
3442
'compass.ocean.tests.global_ocean.wave_mesh',
3543
'scrip_file.template'))

0 commit comments

Comments
 (0)