Skip to content

Commit 61b2c77

Browse files
committed
Update transects to mpas_tools 1.0.0
1 parent e01c41d commit 61b2c77

File tree

3 files changed

+143
-271
lines changed

3 files changed

+143
-271
lines changed

mpas_analysis/ocean/compute_transects_subtask.py

Lines changed: 69 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,15 @@
1818
from mpas_tools.viz import mesh_to_triangles
1919
from mpas_tools.transects import subdivide_great_circle, \
2020
cartesian_to_great_circle_distance
21-
from mpas_tools.viz.transects import find_transect_cells_and_weights, \
21+
from mpas_tools.viz.transect.horiz import (
22+
find_spherical_transect_cells_and_weights,
2223
make_triangle_tree
23-
from mpas_tools.ocean.transects import find_transect_levels_and_weights, \
24-
interp_mpas_to_transect_triangle_nodes, \
25-
interp_transect_grid_to_transect_triangle_nodes
24+
)
25+
from mpas_tools.ocean.viz.transect.vert import (
26+
find_transect_levels_and_weights,
27+
interp_mpas_to_transect_nodes,
28+
interp_transect_grid_to_transect_nodes
29+
)
2630

2731
from mpas_analysis.shared.climatology import RemapMpasClimatologySubtask, \
2832
get_climatology_op_directory
@@ -161,8 +165,8 @@ def __init__(self, mpasClimatologyTask, parentTask, climatologyName,
161165
self.remap = self.obsDatasets.horizontalResolution != 'mpas'
162166
if self.obsDatasets.horizontalResolution == 'mpas' and \
163167
self.verticalComparisonGridName != 'mpas':
164-
raise ValueError('If the horizontal comparison grid is "mpas", the '
165-
'vertical grid must also be "mpas".')
168+
raise ValueError('If the horizontal comparison grid is "mpas", '
169+
'the vertical grid must also be "mpas".')
166170

167171
def setup_and_check(self):
168172
"""
@@ -471,6 +475,14 @@ def _compute_mpas_transects(self, dsMesh):
471475

472476
dsTris = mesh_to_triangles(dsMesh)
473477

478+
layerThickness = dsMesh.layerThickness
479+
bottomDepth = dsMesh.bottomDepth
480+
maxLevelCell = dsMesh.maxLevelCell - 1
481+
if 'minLevelCell' in dsMesh:
482+
minLevelCell = dsMesh.minLevelCell - 1
483+
else:
484+
minLevelCell = xr.zeros_like(maxLevelCell)
485+
474486
triangleTree = make_triangle_tree(dsTris)
475487

476488
for transectName in transectNames:
@@ -493,22 +505,30 @@ def _compute_mpas_transects(self, dsMesh):
493505
else:
494506
transectZ = None
495507

496-
dsMpasTransect = find_transect_cells_and_weights(
497-
dsTransect.lon, dsTransect.lat, dsTris, dsMesh,
498-
triangleTree, degrees=True)
508+
dsMpasTransect = find_spherical_transect_cells_and_weights(
509+
lon_transect=dsTransect.lon,
510+
lat_transect=dsTransect.lat,
511+
ds_tris=dsTris,
512+
ds_mesh=dsMesh,
513+
tree=triangleTree,
514+
degrees=True)
499515

500516
dsMpasTransect = find_transect_levels_and_weights(
501-
dsMpasTransect, dsMesh.layerThickness,
502-
dsMesh.bottomDepth, dsMesh.maxLevelCell - 1,
503-
transectZ)
517+
ds_horiz_transect=dsMpasTransect,
518+
layer_thickness=layerThickness,
519+
bottom_depth=bottomDepth,
520+
min_level_cell=minLevelCell,
521+
max_level_cell=maxLevelCell,
522+
z_transect=transectZ)
504523

505524
if 'landIceFraction' in dsMesh:
506525
interpCellIndices = dsMpasTransect.interpHorizCellIndices
507526
interpCellWeights = dsMpasTransect.interpHorizCellWeights
508527
landIceFraction = dsMesh.landIceFraction.isel(
509528
nCells=interpCellIndices)
510-
landIceFraction = (landIceFraction * interpCellWeights).sum(
511-
dim='nHorizWeights')
529+
landIceFraction = (
530+
landIceFraction * interpCellWeights).sum(
531+
dim='nHorizWeights')
512532
dsMpasTransect['landIceFraction'] = landIceFraction
513533

514534
# use to_netcdf rather than write_netcdf_with_fill because
@@ -517,9 +537,7 @@ def _compute_mpas_transects(self, dsMesh):
517537
dsMpasTransect.to_netcdf(transectInfoFileName)
518538

519539
dsTransectOnMpas = xr.Dataset(dsMpasTransect)
520-
dsTransectOnMpas['x'] = dsMpasTransect.dNode.isel(
521-
nSegments=dsMpasTransect.segmentIndices,
522-
nHorizBounds=dsMpasTransect.nodeHorizBoundsIndices)
540+
dsTransectOnMpas['x'] = dsMpasTransect.dNode
523541

524542
dsTransectOnMpas['z'] = dsMpasTransect.zTransectNode
525543

@@ -545,9 +563,11 @@ def _compute_mpas_transects(self, dsMesh):
545563
dims = dsMask[var].dims
546564
if 'nCells' in dims and 'nVertLevels' in dims:
547565
dsOnMpas[var] = \
548-
interp_mpas_to_transect_triangle_nodes(
566+
interp_mpas_to_transect_nodes(
549567
dsMpasTransect, dsMask[var])
550568

569+
dsOnMpas = self._transpose(dsOnMpas)
570+
551571
outFileName = self.get_remapped_file_name(
552572
season, comparisonGridName=transectName)
553573
dsOnMpas.to_netcdf(outFileName)
@@ -558,13 +578,37 @@ def _interp_obs_to_mpas(self, da, dsMpasTransect, threshold=0.1):
558578
"""
559579
daMask = da.notnull()
560580
da = da.where(daMask, 0.)
561-
da = interp_transect_grid_to_transect_triangle_nodes(
562-
dsMpasTransect, da)
563-
daMask = interp_transect_grid_to_transect_triangle_nodes(
564-
dsMpasTransect, daMask)
581+
da = interp_transect_grid_to_transect_nodes(
582+
ds_transect=dsMpasTransect,
583+
da=da)
584+
daMask = interp_transect_grid_to_transect_nodes(
585+
ds_transect=dsMpasTransect,
586+
da=daMask)
565587
da = (da / daMask).where(daMask > threshold)
566588
return da
567589

590+
@staticmethod
591+
def _transpose(dsOnMpas):
592+
"""
593+
Transpose the data set to have the expected dimension order
594+
"""
595+
dims = dsOnMpas.dims
596+
dimsTransposed = ['nPoints', 'nz',
597+
'nSegments', 'nHalfLevels',
598+
'nHorizLevels', 'nVertLevels',
599+
'nHorizWeights', 'nVertWeights']
600+
601+
# drop any dimensions not in the dataset
602+
dimsTransposed = [dim for dim in dimsTransposed if dim in
603+
dims]
604+
# add any other dimensions at the end
605+
for dim in dims:
606+
if dim not in dimsTransposed:
607+
dimsTransposed.append(dim)
608+
dsOnMpas = dsOnMpas.transpose(*dimsTransposed)
609+
610+
return dsOnMpas
611+
568612

569613
class TransectsObservations(object):
570614
"""
@@ -611,8 +655,9 @@ def __init__(self, config, obsFileNames, horizontalResolution,
611655
observations for a transect
612656
613657
horizontalResolution : str
614-
'obs' for the obs as they are, 'mpas' for the native MPAS mesh, or a
615-
size in km if subdivision of the observational transect is desired.
658+
'obs' for the obs as they are, 'mpas' for the native MPAS mesh, or
659+
a size in km if subdivision of the observational transect is
660+
desired.
616661
617662
transectCollectionName : str
618663
A name that describes the collection of transects (e.g. the name

mpas_analysis/ocean/plot_transect_subtask.py

Lines changed: 5 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,6 @@
2323

2424
from geometric_features import FeatureCollection
2525

26-
from mpas_tools.ocean.transects import get_outline_segments
27-
2826
from mpas_analysis.shared.plot import plot_vertical_section_comparison, \
2927
savefig, add_inset
3028

@@ -397,18 +395,12 @@ def _plot_transect(self, remappedModelClimatology, remappedRefClimatology):
397395

398396
else:
399397
x = 1e-3*remappedModelClimatology.dNode
400-
z = None
398+
z = remappedModelClimatology.zTransectNode
401399
lon = remappedModelClimatology.lonNode
402400
lat = remappedModelClimatology.latNode
403401

404402
remappedModelClimatology['dNode'] = x
405403

406-
# flatten the x, lon and lat arrays because this is what
407-
# vertical_section is expecting
408-
x = xr.DataArray(data=x.values.ravel(), dims=('nx',))
409-
lon = xr.DataArray(data=lon.values.ravel(), dims=('nx',))
410-
lat = xr.DataArray(data=lat.values.ravel(), dims=('nx',))
411-
412404
# This will do strange things at the antemeridian but there's little
413405
# we can do about that.
414406
lon_pm180 = numpy.mod(lon + 180., 360.) - 180.
@@ -437,12 +429,6 @@ def _plot_transect(self, remappedModelClimatology, remappedRefClimatology):
437429

438430
modelOutput = remappedModelClimatology[self.mpasFieldName]
439431

440-
if remap:
441-
triangulation_args = None
442-
else:
443-
triangulation_args = self._get_ds_triangulation(
444-
remappedModelClimatology)
445-
446432
if remappedRefClimatology is None:
447433
refOutput = None
448434
bias = None
@@ -578,12 +564,11 @@ def _plot_transect(self, remappedModelClimatology, remappedRefClimatology):
578564
configSectionName,
579565
xCoords=xs,
580566
zCoord=z,
581-
triangulation_args=triangulation_args,
582567
colorbarLabel=self.unitsLabel,
583568
xlabels=xLabels,
584569
ylabel=yLabel,
585570
title=title,
586-
modelTitle='{}'.format(mainRunName),
571+
modelTitle=mainRunName,
587572
refTitle=self.refTitleLabel,
588573
diffTitle=self.diffTitleLabel,
589574
numUpperTicks=numUpperTicks,
@@ -694,7 +679,7 @@ def _lat_greater_extent(self, lat, lon):
694679
maxes = []
695680
last_idx = 0
696681

697-
while(len(lon_r) > 0 and len(lon_l) > 0):
682+
while len(lon_r) > 0 and len(lon_l) > 0:
698683
if lon_r[0] < lon_l[0]:
699684
mins.append(numpy.min(lon[last_idx:lon_r[0]]))
700685
last_idx = lon_r[0]
@@ -741,7 +726,8 @@ def _strictly_monotonic(self, coord):
741726
# Greg Streletz, Xylar Asay-Davis
742727

743728
coord_diff = numpy.diff(coord.values)
744-
coord_diff = numpy.where(coord_diff > 180, coord_diff - 360, coord_diff)
729+
coord_diff = numpy.where(coord_diff > 180, coord_diff - 360,
730+
coord_diff)
745731
coord_diff = numpy.where(coord_diff < -180, coord_diff + 360,
746732
coord_diff)
747733
return numpy.all(coord_diff > 0) or numpy.all(coord_diff < 0)
@@ -838,24 +824,6 @@ def _lat_fewest_direction_changes(self, lat, lon):
838824
else:
839825
return False
840826

841-
def _get_ds_triangulation(self, dsTransectTriangles):
842-
"""get matplotlib Triangulation from triangulation dataset"""
843-
844-
nTransectTriangles = dsTransectTriangles.sizes['nTransectTriangles']
845-
dNode = dsTransectTriangles.dNode.isel(
846-
nSegments=dsTransectTriangles.segmentIndices,
847-
nHorizBounds=dsTransectTriangles.nodeHorizBoundsIndices)
848-
x = dNode.values.ravel()
849-
850-
zTransectNode = dsTransectTriangles.zTransectNode
851-
y = zTransectNode.values.ravel()
852-
853-
tris = numpy.arange(3 * nTransectTriangles).reshape(
854-
(nTransectTriangles, 3))
855-
triangulation_args = dict(x=x, y=y, triangles=tris)
856-
857-
return triangulation_args
858-
859827
@staticmethod
860828
def _get_contour_colormap():
861829
# https://stackoverflow.com/a/18926541/7728169

0 commit comments

Comments
 (0)