77
88import sys ,os ,types
99import logging
10+ import pathlib # python 3 only, but python 2 is not worth supporting anymore.
1011
1112try :
1213 from osgeo import ogr ,osr
@@ -76,6 +77,14 @@ def __init__(self,*a,meshes=[],**k):
7677 super ().__init__ (* a ,** k )
7778 self .meshes = meshes
7879
80+ class DuplicateMeshElement (GridException ):
81+ def __init__ (self , dest_mesh , * a , dest_cell = - 1 , src_mesh = None , src_cell = - 1 , ** k ):
82+ super ().__init__ (* a ,** k )
83+ self .dest_mesh = dest_mesh
84+ self .src_mesh = src_mesh
85+ self .dest_cell = dest_cell
86+ self .src_cell = src_cell
87+
7988def request_square (ax ,max_bounds = None ):
8089 """
8190 Attempt to set a square aspect ratio on matplotlib axes ax
@@ -521,6 +530,9 @@ def modify_max_sides(self,max_sides):
521530 if not np .all ( self .cells ['nodes' ][:,max_sides :] == self .UNDEFINED ):
522531 raise GridException ("Some cells cannot fit in requested max_sides" )
523532
533+ # DFM hack, sometimes we have other cell:edge variables.
534+ cell_side_fields = ['edges' ,'nodes' ,'NetElemLink' ]
535+
524536 old_max_sides = self .max_sides
525537 self .max_sides = max_sides
526538
@@ -537,7 +549,7 @@ def modify_max_sides(self,max_sides):
537549 else :
538550 shape = None
539551
540- if name in [ 'edges' ,'nodes' ]:
552+ if name in cell_side_fields : # 'edges','nodes','NetElemLink'
541553 new_cell_dtype .append ( (name ,vtype ,self .max_sides ) )
542554 else :
543555 new_cell_dtype .append ( typeinfo ) # just copy
@@ -549,7 +561,7 @@ def modify_max_sides(self,max_sides):
549561
550562 for typeinfo in old_dtype :
551563 name = typeinfo [0 ]
552- if name in [ 'edges' , 'nodes' ]:
564+ if name in cell_side_fields :
553565 if old_max_sides > self .max_sides :
554566 new_cells [name ][:,:] = self .cells [name ][:,:self .max_sides ]
555567 else :
@@ -721,6 +733,9 @@ def from_ugrid(nc,mesh_name=None,skip_edges=False,fields='auto',
721733 face_node_connectivity, edge_node_connectivity, and optionally face_edge
722734 connectivity, edge_face_connectivity, etc.)
723735 """
736+ if isinstance (nc ,pathlib .Path ):
737+ nc = str (nc )
738+
724739 if isinstance (nc ,six .string_types ):
725740 filename = nc
726741 if dialect == 'fvcom' :
@@ -2906,7 +2921,13 @@ def bad_fields(Adata,Bdata): # field froms B which get dropped
29062921 for i ,edge in enumerate (kwargs ['edges' ]):
29072922 kwargs ['edges' ][i ]= edge_map [edge ]
29082923
2909- cell_map [n ]= self .add_cell (** kwargs )
2924+ try :
2925+ cell_map [n ]= self .add_cell (** kwargs )
2926+ except DuplicateMeshElement as exc :
2927+ exc .src_mesh = ugB
2928+ exc .src_cell = n
2929+ exc .dest_mesh = self
2930+ raise
29102931
29112932 return node_map ,edge_map ,cell_map
29122933
@@ -4911,13 +4932,17 @@ def add_cell(self,**kwargs):
49114932 if ( (n1 == self .edges ['nodes' ][j ,0 ]) and
49124933 (n2 == self .edges ['nodes' ][j ,1 ]) ):
49134934 # this cell is on the 'left' side of the edge
4914- assert self .edges ['cells' ][j ,0 ]< 0
4935+ # assert self.edges['cells'][j,0]<0
4936+ if self .edges ['cells' ][j ,0 ]>= 0 :
4937+ raise DuplicateMeshElement (dest_mesh = self , dest_cell = self .edges ['cells' ][j ,0 ])
49154938 # TODO: probably this ought to be using modify_edge
49164939 self .edges ['cells' ][j ,0 ]= i
49174940 elif ( (n1 == self .edges ['nodes' ][j ,1 ]) and
49184941 (n2 == self .edges ['nodes' ][j ,0 ]) ):
49194942 # this cell is on the 'right' side of the edge
4920- assert self .edges ['cells' ][j ,1 ]< 0
4943+ # assert self.edges['cells'][j,1]<0
4944+ if self .edges ['cells' ][j ,1 ]>= 0 :
4945+ raise DuplicateMeshElement (dest_mesh = self , dest_cell = self .edges ['cells' ][j ,1 ])
49214946 # TODO: probably this ought to be using modify_edge
49224947 self .edges ['cells' ][j ,1 ]= i
49234948 else :
@@ -5925,6 +5950,32 @@ def smooth_matrix(self,f=0.5,K='scaled',dx='grid',V='grid',A='grid',dt=None):
59255950
59265951 return D .tocsr ()
59275952
5953+ def fill_by_smoothing_func (self ,cells ,count = 7 ):
5954+ """
5955+ Return a function that takes a subset of cell values and returns a dense, interpolated
5956+ field across all the cells of a grid.
5957+ cells: cell indexes where data is known
5958+ count: iterations of filling
5959+ """
5960+ M = self .smooth_matrix ()
5961+ def fill_func (subset ,cells = cells ,M = M ,count = count ):
5962+ v = np .full (self .Ncells (), 0.0 ) # values
5963+ w = np .full (self .Ncells (), 0.0 ) # weights
5964+ valid = np .isfinite (subset )
5965+ v [cells [valid ]] = subset [valid ]
5966+ w [cells [valid ]] = 1.0
5967+
5968+ for _ in range (count ):
5969+ v = M .dot (v )
5970+ w = M .dot (w )
5971+
5972+ thresh = 0.01
5973+ result = v / w .clip (thresh )
5974+ result [ w < thresh ]= np .nan
5975+ return result
5976+ return fill_func
5977+
5978+
59285979 def edge_clip_mask (self ,xxyy ,ends = False ):
59295980 """
59305981 return a bitmask over edges falling in the boundiny box.
@@ -6014,7 +6065,7 @@ def plot_cells(self,ax=None,mask=None,values=None,clip=None,centers=False,labele
60146065 values = np .asanyarray (values )
60156066
60166067 if ragged_edges is None :
6017- ragged_edges = 'edgecolor' in kwargs
6068+ ragged_edges = 'edgecolor' in kwargs or 'ec' in kwargs
60186069
60196070 if mask is None :
60206071 mask = ~ self .cells ['deleted' ]
0 commit comments