18
18
from mpas_tools .viz import mesh_to_triangles
19
19
from mpas_tools .transects import subdivide_great_circle , \
20
20
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 ,
22
23
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
+ )
26
30
27
31
from mpas_analysis .shared .climatology import RemapMpasClimatologySubtask , \
28
32
get_climatology_op_directory
@@ -161,8 +165,8 @@ def __init__(self, mpasClimatologyTask, parentTask, climatologyName,
161
165
self .remap = self .obsDatasets .horizontalResolution != 'mpas'
162
166
if self .obsDatasets .horizontalResolution == 'mpas' and \
163
167
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".' )
166
170
167
171
def setup_and_check (self ):
168
172
"""
@@ -471,6 +475,14 @@ def _compute_mpas_transects(self, dsMesh):
471
475
472
476
dsTris = mesh_to_triangles (dsMesh )
473
477
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
+
474
486
triangleTree = make_triangle_tree (dsTris )
475
487
476
488
for transectName in transectNames :
@@ -493,22 +505,30 @@ def _compute_mpas_transects(self, dsMesh):
493
505
else :
494
506
transectZ = None
495
507
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 )
499
515
500
516
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 )
504
523
505
524
if 'landIceFraction' in dsMesh :
506
525
interpCellIndices = dsMpasTransect .interpHorizCellIndices
507
526
interpCellWeights = dsMpasTransect .interpHorizCellWeights
508
527
landIceFraction = dsMesh .landIceFraction .isel (
509
528
nCells = interpCellIndices )
510
- landIceFraction = (landIceFraction * interpCellWeights ).sum (
511
- dim = 'nHorizWeights' )
529
+ landIceFraction = (
530
+ landIceFraction * interpCellWeights ).sum (
531
+ dim = 'nHorizWeights' )
512
532
dsMpasTransect ['landIceFraction' ] = landIceFraction
513
533
514
534
# use to_netcdf rather than write_netcdf_with_fill because
@@ -517,9 +537,7 @@ def _compute_mpas_transects(self, dsMesh):
517
537
dsMpasTransect .to_netcdf (transectInfoFileName )
518
538
519
539
dsTransectOnMpas = xr .Dataset (dsMpasTransect )
520
- dsTransectOnMpas ['x' ] = dsMpasTransect .dNode .isel (
521
- nSegments = dsMpasTransect .segmentIndices ,
522
- nHorizBounds = dsMpasTransect .nodeHorizBoundsIndices )
540
+ dsTransectOnMpas ['x' ] = dsMpasTransect .dNode
523
541
524
542
dsTransectOnMpas ['z' ] = dsMpasTransect .zTransectNode
525
543
@@ -545,9 +563,11 @@ def _compute_mpas_transects(self, dsMesh):
545
563
dims = dsMask [var ].dims
546
564
if 'nCells' in dims and 'nVertLevels' in dims :
547
565
dsOnMpas [var ] = \
548
- interp_mpas_to_transect_triangle_nodes (
566
+ interp_mpas_to_transect_nodes (
549
567
dsMpasTransect , dsMask [var ])
550
568
569
+ dsOnMpas = self ._transpose (dsOnMpas )
570
+
551
571
outFileName = self .get_remapped_file_name (
552
572
season , comparisonGridName = transectName )
553
573
dsOnMpas .to_netcdf (outFileName )
@@ -558,13 +578,37 @@ def _interp_obs_to_mpas(self, da, dsMpasTransect, threshold=0.1):
558
578
"""
559
579
daMask = da .notnull ()
560
580
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 )
565
587
da = (da / daMask ).where (daMask > threshold )
566
588
return da
567
589
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
+
568
612
569
613
class TransectsObservations (object ):
570
614
"""
@@ -611,8 +655,9 @@ def __init__(self, config, obsFileNames, horizontalResolution,
611
655
observations for a transect
612
656
613
657
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.
616
661
617
662
transectCollectionName : str
618
663
A name that describes the collection of transects (e.g. the name
0 commit comments