- Timestamp:
- 07/15/24 11:11:01 (5 months ago)
- Location:
- TOOLS/MOSAIX
- Files:
-
- 6 edited
Legend:
- Unmodified
- Added
- Removed
-
TOOLS/MOSAIX/Build_coordinates_mask.py
r6190 r6899 88 88 if nperio != 0 : 89 89 if nperio != 6 : 90 print (f'Warning ! model = {model} and ocePerio = {nperio} instead of 6 !')90 print (f'Warning ! model = {model} and ocePerio = {nperio} instead of 6 !') 91 91 else : 92 92 nperio = 6 … … 95 95 if nperio != 0 : 96 96 if nperio != 4 : 97 print (f'Attention ! model = {model} and ocePerio = {nperio} instead of 4 !')97 print (f'Attention ! model = {model} and ocePerio = {nperio} instead of 4 !') 98 98 else : 99 99 nperio = 4 100 100 101 print (f' model = {model}\n ocePerio = {nperio}\n bathy = {n_bathy}\n coord = {n_coord}\n fout = {n_out}')102 print (f' Switchs : nemo4Ubug = {nemoUbug}, straits = {straits}, coordPerio = {coordperio}, maskbathy = {maskbathynemo}')101 print (f' model = {model}\n ocePerio = {nperio}\n bathy = {n_bathy}\n coord = {n_coord}\n fout = {n_out}') 102 print (f' Switchs : nemo4Ubug = {nemoUbug}, straits = {straits}, coordPerio = {coordperio}, maskbathy = {maskbathynemo}') 103 103 104 104 ## -
TOOLS/MOSAIX/CalvingWeights.py
r6666 r6899 78 78 # Adding arguments 79 79 parser.add_argument ('--oce' , help='oce model name', type=str, default='eORCA1.2', 80 choices=['ORCA2.3', 'ORCA1.0', 'ORCA1.1', 'ORCA1_CNRM', 'eORCA1.2', 80 choices=['ORCA2.3', 'ORCA2.4', 'ORCA2.4.2', 81 'ORCA1.0', 'ORCA1.1', 'ORCA1_CNRM', 'eORCA1.2', 81 82 'eORCA1.4', 'eORCA1.4.2', 'eORCA025', 'eORCA025.1', 'eORCA025.4'] ) 82 83 parser.add_argument ('--atm' , type=str, default='ICO40', … … 178 179 # Periodicity masking for NEMO 179 180 if nperio_dst == 0 : 181 if dst_Name in ['ORCA2.4.2',] : nperio_dst = 4.2 180 182 if dst_Name in ['ORCA2.3', 'ORCA2.4'] : nperio_dst = 4 181 183 if dst_Name in ['eORCA1.2', 'eORCA1.3'] : nperio_dst = 6 … … 356 358 # 357 359 #print ( 'Sum of remap_matrix : {:12.3e}'.format(np.sum(matrix_local)) ) 358 print ( 'Point in atmospheric grid : {index_src:4d} -- num_links: {num_links:6d}' )360 print ( f'Point in atmospheric grid : {index_src:4d} -- num_links: {num_links:6d}' ) 359 361 print (' ') 360 362 -
TOOLS/MOSAIX/CreateWeightsMask.bash
r6666 r6899 75 75 echo ${Titre}"Defines model"${Norm} 76 76 # ================================= 77 CplModel=ORCA2.3xLMD9695 77 #CplModel=ORCA2.3xLMD9695 78 CplModel=ORCA2.4.2xLMD9695 78 79 #CplModel=ORCA2.3xICO30 79 80 #CplModel=ORCA2.3xICO40 … … 88 89 #CplModel=eORCA025.1xLMD256256 89 90 90 #Version="v0" ; Comment="Fully tested in IPSLCM6 eORCA1.2 x LMD 144x142"91 Version="v0" ; Comment="Test ORCA 2.4.2" 91 92 #Version="v1" ; Comment="Fully tested in IPSLCM6 eORCA1.2 x LMD 144x142" 92 93 #Version="NoSearchRadius" ; Comment="For testing new routing" 93 Version="v3" ; Comment="Correction of ORCA masks to have a perfect conservation of run-off"94 #Version="v3" ; Comment="Correction of ORCA masks to have a perfect conservation of run-off" 94 95 95 96 # If available, get model name from job name … … 326 327 327 328 case ${OCE} in # Periodicity type of ORCA grid 329 ( ORCA2.4.2 ) OcePerio=6.2 ;; # 4 ORCA 6 PALEORCA 328 330 ( ORCA2.4 ) OcePerio=4.2 ;; # 4 ORCA 6 PALEORCA 329 331 ( ORCA2* ) OcePerio=4 ;; # 4 ORCA 6 PALEORCA -
TOOLS/MOSAIX/README.md
r6235 r6899 1 1 # MOSAIX 2 3 ## MOSAIX 4 5 MOSAIX is a small set of fortran routines, bash scripts, python 6 scripts and xml files allowing to generate coupling weights for the 7 IPSL Earth System Model. It replaces the old MOSAIC 8 software. It uses XIOS as a library to compute weights. In the present 9 state, it can handle ORCA, LMDZ lon/lat and DYNAMICO grids. 10 11 MOSAIX can generate second order conservative interpolation 12 weights. They are used in place of bilinear interpolation by 13 [OASIS-MCT](https://portal.enes.org/oasis) 14 15 As MOSAIX uses XIOS, it works in parallel, using MPI distributed memory. 16 2 17 3 18 ## SVN information … … 37 52 to the following warning : 38 53 39 ## #Warning54 ## Warning 40 55 41 56 Warning, to install, configure, run, use any of MOSAIX software or to … … 47 62 software by incorrectly or partially configured 48 63 49 ## MOSAIX50 51 MOSAIX is a small set of fortran routines, bash scripts, python52 scripts and xml files allowing to generate coupling weights for the53 IPSL Earth System Model. It is under development, and presently not54 used by the production runs. It aims to replace the old MOSAIC55 software. It uses XIOS as a library to compute weights. In the present56 state, it can handle ORCA, LMDZ lon/lat and DYNAMICO grids.57 58 MOSAIX can generate second order conservative interpolation59 weights. They are used in place of bilinear interpolation by60 [OASIS-MCT](https://portal.enes.org/oasis)61 62 As MOSAIX uses XIOS, it works in parallel, using MPI distributed memory.63 64 64 65 ## Compatibility with MOSAIC … … 83 84 84 85 MOSAIX been developped and extensively used on Irene Skylake computer, 85 using the Intel compilation tools. MOSAIX is supposed to work on any 86 computer where XIOS works. 86 using the Intel compilation tools. Irene AMD computer is also commonly used. MOSAIX is supposed to work on any computer where XIOS works. 87 87 88 88 It has been tested on Mac OS X, using `gcc` as a compiler. It is 89 fragile, as regularly XIOS updates break the compatibility.89 fragile, as regularly XIOS or Mac OS updates break the compatibility. 90 90 91 91 ## TODO … … 231 231 - _oorc_. ORCA grid at _T_ point. Masked on land, duplicated (from 232 232 periodicity) point masked, with area equal 1. 233 234 ## Understanding ORCA periodicity parameter in MOSAIX (`OcePerio`) 235 236 ORCA grids are global, with an east-west periodicity, and a special 237 fold at the north pole. See https://www.nemo-ocean.eu for mode details. 238 239 Depending on the NEMO code version, and ot the ORCA 240 configuration, ones may have different situations : 241 242 ### North pole folding 243 - Old versions of ORCA have a north pole folding around a T point. 244 - Newer versions of ORCA have a north pole folding around a F point. 245 246 ### Halos 247 - NEMO version < 4.2 have grid file that contains the halo points at 248 the east-west periodidicity and at the upper line. 249 - Since version >= 4.2, grid files does not include halo points. 250 251 ### Values MOSAIX periodicity parameter `OcePerio` 252 253 254 OcePerio | NEMO<4.2 | NEMO>=4.2 | 255 |----------------------------------|--------------|----------------| 256 | Old ORCA grids (T-point folding) | `OcePerio=4` | `OcePerio=4.2` | 257 | New ORCA grids (F-point folding) | `OcePerio=6` | `OcePerio=6.2` | 258 233 259 234 260 ## Generating input grid files -
TOOLS/MOSAIX/RunoffWeights.py
r6666 r6899 101 101 # Adding arguments 102 102 parser.add_argument ('--oce' , help='oce model name', type=str, default='eORCA1.2', 103 choices=['ORCA2.3', 'ORCA1.0', 'ORCA1.1', 'ORCA1_CNRM', 'eORCA1.2', 104 'eORCA1.4', 'eORCA1.4.2', 'eORCA025', 'eORCA025.1', 'eORCA025.4'] ) 103 choices=['ORCA2.3', 'ORCA2.4', 'ORCA2.4.2', 104 'ORCA1.0', 'ORCA1.1', 'ORCA1_CNRM', 'eORCA1.2', 105 'eORCA1.4', 'eORCA1.4.2', 106 'eORCA025', 'eORCA025.1', 'eORCA025.4'] ) 105 107 parser.add_argument ('--atm' , help='atm model name', type=str, default='LMD9695' ) 106 108 parser.add_argument ('--atmCoastWidth', type=int, default=1, … … 231 233 # if oce_jpi == 362 : oce_perio = 6 # ORCA 1 232 234 # if oce_jpi == 1442 : oce_perio = 6 # ORCA 025 233 oce_p reio = nemo.__guess_nperio__ (oce_jpj, oce_jpi, nperio=oce_perio)235 oce_perio = nemo.__guess_nperio__ (oce_jpj, oce_jpi, nperio=oce_perio) 234 236 235 237 print ( f'Oce NPERIO parameter : {oce_perio}' ) -
TOOLS/MOSAIX/nemo.py
r6666 r6899 1 # -*- coding: utf-8 -*-1 7# -*- coding: utf-8 -*- 2 2 ## =========================================================================== 3 3 ## … … 18 18 ## 19 19 ## =========================================================================== 20 '''Utilities to plot NEMO ORCA fields, 20 ''' 21 Utilities to plot NEMO ORCA fields, 21 22 22 23 Handles periodicity and other stuff … … 38 39 import xarray as xr 39 40 40 # Tries to import some moldules that are available in all Python installations41 # and used in only a few specific functions42 41 try : 43 42 from sklearn.impute import SimpleImputer … … 68 67 REPSI = np.finfo (1.0).eps 69 68 69 RAAMO = xr.DataArray (12) ; RAAMO .name='RAAMO' ; RAAMO.attrs.update ({'units':"mth" , 'long_name':"Number of months in one year" }) 70 RJJHH = xr.DataArray (24) ; RJJHH .name='RJJHH' ; RJJHH.attrs.update ({'units':"h" , 'long_name':"Number of hours in one day"} ) 71 RHHMM = xr.DataArray (60) ; RHHMM .name='RHHMM' ; RHHMM.attrs.update ({'units':"min" , 'long_name':"Number of minutes in one hour"} ) 72 RMMSS = xr.DataArray (60) ; RMMSS .name='RMMSS' ; RMMSS.attrs.update ({'units':"s" , 'long_name':"Number of seconds in one minute"} ) 73 RA = xr.DataArray (6371229.0) ; RA .name='RA' ; RA.attrs.update ({'units':"m" , 'long_name':"Earth radius"} ) 74 GRAV = xr.DataArray (9.80665) ; GRAV .name='GRAV' ; GRAV.attrs.update ({'units':"m/s2" , 'long_name':"Gravity"} ) 75 RT0 = xr.DataArray (273.15) ; RT0 .name='RT0' ; RT0.attrs.update ({'units':"K" , 'long_name':"Freezing point of fresh water"} ) 76 RAU0 = xr.DataArray (1026.0) ; RAU0 .name='RAU0' ; RAU0.attrs.update ({'units':"kg/m3" , 'long_name':"Volumic mass of sea water"} ) 77 SICE = xr.DataArray (6.0) ; SICE .name='SICE' ; SICE.attrs.update ({'units':"psu" , 'long_name':"Salinity of ice (for pisces)"} ) 78 SOCE = xr.DataArray (34.7) ; SOCE .name='SOCE' ; SOCE.attrs.update ({'units':"psu" , 'long_name':"Salinity of sea (for pisces and isf)"} ) 79 RLEVAP = xr.DataArray (2.5e+6) ; RLEVAP.name='RLEVAP' ; RLEVAP.attrs.update ({'units':"J/K" , 'long_name':"Latent heat of evaporation (water)"} ) 80 VKARMN = xr.DataArray (0.4) ; VKARMN.name='VKARMN' ; VKARMN.attrs.update ({'units':"-" , 'long_name':"Von Karman constant"} ) 81 STEFAN = xr.DataArray (5.67e-8) ; STEFAN.name='STEFAN' ; STEFAN.attrs.update ({'units':"W/m2/K4", 'long_name':"Stefan-Boltzmann constant"} ) 82 RHOS = xr.DataArray (330.) ; RHOS .name='RHOS' ; RHOS.attrs.update ({'units':"kg/m3" , 'long_name':"Volumic mass of snow"} ) 83 RHOI = xr.DataArray (917.) ; RHOI .name='RHOI' ; RHOI.attrs.update ({'units':"kg/m3" , 'long_name':"Volumic mass of sea ice"} ) 84 RHOW = xr.DataArray (1000.) ; RHOW .name='RHOW' ; RHOW.attrs.update ({'units':"kg/m3" , 'long_name':"Volumic mass of freshwater in melt ponds"} ) 85 RCND_I = xr.DataArray (2.034396) ; RCND_I.name='RCND_I' ; RCND_I.attrs.update ({'units':"W/m/J" , 'long_name':"Thermal conductivity of fresh ice"} ) 86 RCPI = xr.DataArray (2067.0) ; RCPI .name='RCPI' ; RCPI.attrs.update ({'units':"J/kg/K" , 'long_name':"Specific heat of fresh ice"} ) 87 RLSUB = xr.DataArray (2.834e+6) ; RLSUB .name='RLSUB' ; RLSUB.attrs.update ({'units':"J/kg" , 'long_name':"Pure ice latent heat of sublimation"} ) 88 RLFUS = xr.DataArray (0.334e+6) ; RLFUS .name='RLFUS' ; RLFUS.attrs.update ({'units':"J/kg" , 'long_name':"Latent heat of fusion of fresh ice"} ) 89 RTMLT = xr.DataArray (0.054) ; RTMLT .name='RTMLT' ; RTMLT.attrs.update ({'units':"C/PSU" , 'long_name':"Decrease of seawater meltpoint with salinity"} ) 90 91 RDAY = RJJHH * RHHMM * RMMSS ; RDAY .name='RDAY' ; RDAY.attrs.update ({'units':"s" , 'long_name':"Day length"} ) 92 RSIYEA = 365.25 * RDAY * 2. * RPI / 6.283076 ; RSIYEA.name='RSIYEA' ; RSIYEA.attrs.update ({'units':"s" , 'long_name':"Sideral year length"} ) 93 RSIDAY = RDAY / (1. + RDAY / RSIYEA) ; RSIDAY.name='RSIDAY' ; RSIDAY.attrs.update ({'units':"s" , 'long_name':"Sideral day length"} ) 94 ROMEGA = 2. * RPI / RSIDAY ; ROMEGA.name='ROMEGA' ; ROMEGA.attrs.update ({'units':"s-1" , 'long_name':"Earth rotation parameter"} ) 95 70 96 NPERIO_VALID_RANGE = [0, 1, 4, 4.2, 5, 6, 6.2] 71 97 72 RAAMO = 12 # Number of months in one year73 RJJHH = 24 # Number of hours in one day74 RHHMM = 60 # Number of minutes in one hour75 RMMSS = 60 # Number of seconds in one minute76 RA = 6371229.0 # Earth radius [m]77 GRAV = 9.80665 # Gravity [m/s2]78 RT0 = 273.15 # Freezing point of fresh water [Kelvin]79 RAU0 = 1026.0 # Volumic mass of sea water [kg/m3]80 SICE = 6.0 # Salinity of ice (for pisces) [psu]81 SOCE = 34.7 # Salinity of sea (for pisces and isf) [psu]82 RLEVAP = 2.5e+6 # Latent heat of evaporation (water) [J/K]83 VKARMN = 0.4 # Von Karman constant84 STEFAN = 5.67e-8 # Stefan-Boltzmann constant [W/m2/K4]85 RHOS = 330. # Volumic mass of snow [kg/m3]86 RHOI = 917. # Volumic mass of sea ice [kg/m3]87 RHOW = 1000. # Volumic mass of freshwater in melt ponds [kg/m3]88 RCND_I = 2.034396 # Thermal conductivity of fresh ice [W/m/K]89 RCPI = 2067.0 # Specific heat of fresh ice [J/kg/K]90 RLSUB = 2.834e+6 # Pure ice latent heat of sublimation [J/kg]91 RLFUS = 0.334e+6 # Latent heat of fusion of fresh ice [J/kg]92 RTMLT = 0.054 # Decrease of seawater meltpoint with salinity93 94 RDAY = RJJHH * RHHMM * RMMSS # Day length [s]95 RSIYEA = 365.25 * RDAY * 2. * RPI / 6.283076 # Sideral year length [s]96 RSIDAY = RDAY / (1. + RDAY / RSIYEA) # Sideral day length [s]97 OMEGA = 2. * RPI / RSIDAY # Earth rotation parameter [s-1]98 99 98 ## Default names of dimensions 100 UDIMS = {'x':'x', 'y':'y', 'z':'olevel', 't':'time_counter'} 101 102 ## All possibles name of dimensions in Nemo files 99 UDIMS = {'x':'x', 'y':'y', 'z':'olevel', 't':'time_counter' } 100 101 ## All possible names of lon/lat variables in Nemo files 102 LONNAME=['nav_lon', 'nav_lon_T', 'nav_lon_U', 'nav_lon_V', 'nav_lon_F', 'nav_lon_W', 103 'nav_lon_grid_T', 'nav_lon_grid_U', 'nav_lon_grid_V', 'nav_lon_grid_F', 'nav_lon_grid_W'] 104 LATNAME=['nav_lat', 'nav_lat_T', 'nav_lat_U', 'nav_lat_V', 'nav_lat_F', 'nav_lat_W', 105 'nav_lat_grid_T', 'nav_lat_grid_U', 'nav_lat_grid_V', 'nav_lat_grid_F', 'nav_lat_grid_W'] 106 107 ## All possibles names of dimensions in Nemo files 103 108 XNAME = [ 'x', 'X', 'X1', 'xx', 'XX', 104 109 'x_grid_T', 'x_grid_U', 'x_grid_V', 'x_grid_F', 'x_grid_W', 105 'lon', 'nav_lon', 'longitude', 'X1', 'x_c', 'x_f' ,]110 'lon', 'nav_lon', 'longitude', 'X1', 'x_c', 'x_f'] 106 111 YNAME = [ 'y', 'Y', 'Y1', 'yy', 'YY', 107 112 'y_grid_T', 'y_grid_U', 'y_grid_V', 'y_grid_F', 'y_grid_W', 108 'lat', 'nav_lat', 'latitude' , 'Y1', 'y_c', 'y_f' ,]113 'lat', 'nav_lat', 'latitude' , 'Y1', 'y_c', 'y_f'] 109 114 ZNAME = [ 'z', 'Z', 'Z1', 'zz', 'ZZ', 'depth', 'tdepth', 'udepth', 110 115 'vdepth', 'wdepth', 'fdepth', 'deptht', 'depthu', 111 116 'depthv', 'depthw', 'depthf', 'olevel', 'z_c', 'z_f', ] 112 TNAME = [ 't', 'T', 'tt', 'TT', 'time', 'time_counter', 'time_centered', ]113 114 ## All possibles name of units of dimensions in N emofiles117 TNAME = [ 't', 'T', 'tt', 'TT', 'time', 'time_counter', 'time_centered', 'time', 'TIME', 'TIME_COUNTER', 'TIME_CENTERED', ] 118 119 ## All possibles name of units of dimensions in NEMO files 115 120 XUNIT = [ 'degrees_east', ] 116 121 YUNIT = [ 'degrees_north', ] … … 119 124 120 125 ## All possibles size of dimensions in Orca files 121 XLENGTH = [ 180, 182, 360, 362, 1440 ] 122 YLENGTH = [ 148, 149, 331, 332 ] 123 ZLENGTH = [ 31, 75] 126 XLENGTH = [ 180, 182, 360, 362, 1440, 1442 ] 127 YLENGTH = [ 148, 149, 331, 332 ] 128 ZLENGTH = [ 31, 75] 129 XYZLENGTH = [ [180,148,31], [182,149,31], [360,331,75], [362,332,75] ] 130 131 ## T, S array to plot TS diagrams 132 Ttab = np.linspace (-2, 40, 100) 133 Stab = np.linspace ( 0, 40, 100) 134 135 Ttab = xr.DataArray ( Ttab, dims=('Temperature',), coords=(Ttab,) ) 136 Stab = xr.DataArray ( Stab, dims=('Salinity' ,), coords=(Stab,) ) 137 138 Ttab.attrs.update ( {'unit':'degrees_celcius', 'long_name':'Temperature'} ) 139 Stab.attrs.update ( {'unit':'PSU', 'long_name':'Salinity'} ) 140 141 # nemo internal options 142 import warnings 143 from typing import TYPE_CHECKING, Literal, TypedDict 144 145 Stack = list() 146 147 if TYPE_CHECKING : 148 Options = Literal [ "Debug", "Trace", "Depth", "Stack" ] 149 150 class T_Options (TypedDict) : 151 Debug = bool 152 Trace = bool 153 Depth = int 154 Stack = list() 155 156 OPTIONS = { 'Debug':False, 'Trace':False, 'Depth':-1, 'Stack':list() } 157 158 class set_options : 159 """ 160 Set options for nemo 161 """ 162 def __init__ (self, **kwargs): 163 self.old = {} 164 for k, v in kwargs.items(): 165 if k not in OPTIONS: 166 raise ValueError ( f"argument name {k!r} is not in the set of valid options {set(OPTIONS)!r}" ) 167 self.old[k] = OPTIONS[k] 168 self._apply_update(kwargs) 169 170 def _apply_update (self, options_dict) : OPTIONS.update (options_dict) 171 def __enter__ (self) : return 172 def __exit__ (self, type, value, traceback) : self._apply_update (self.old) 173 174 def get_options () -> dict : 175 """ 176 Get options for nemo 177 178 See Also 179 ---------- 180 set_options 181 182 """ 183 return OPTIONS 184 185 def return_stack () : 186 return Stack 187 188 def PushStack (string:str) : 189 OPTIONS['Depth'] += 1 190 if OPTIONS['Trace'] : print ( ' '*OPTIONS['Depth'], '-->nemo:', string) 191 Stack.append (string) 192 return 193 194 def PopStack (string:str) : 195 if OPTIONS['Trace'] : print ( ' '*OPTIONS['Depth'], '<--nemo:', string) 196 OPTIONS['Depth'] -= 1 197 Stack.pop () 198 return 124 199 125 200 ## =========================================================================== … … 129 204 Returns type 130 205 ''' 206 PushStack ( f'__mmath__ ( ptab, {default=} )' ) 207 131 208 mmath = default 132 if isinstance (ptab, xr.core.dataarray.DataArray) : 133 mmath = xr 134 if isinstance (ptab, np.ndarray) : 135 mmath = np 136 if isinstance (ptab, np.ma.MaskType) : 137 mmath = np.ma 138 209 if isinstance (ptab, xr.core.dataset.Dataset) : mmath = 'dataset' 210 if isinstance (ptab, xr.core.dataarray.DataArray) : mmath = xr 211 if isinstance (ptab, np.ndarray) : mmath = np 212 if isinstance (ptab, np.ma.MaskType) : mmath = np.ma 213 214 PopStack ( f'__math_ : {mmath}' ) 139 215 return mmath 140 216 141 def __guess_nperio__ (jpj , jpi, nperio=None, out='nperio') :217 def __guess_nperio__ (jpj:int, jpi:int, nperio=None, out:str='nperio') : 142 218 '''Tries to guess the value of nperio (periodicity parameter. 143 219 … … 148 224 nperio : periodicity parameter 149 225 ''' 226 PushStack ( f'__guess_nperio__ ( {jpj=} {jpi=} {nperio=}, {out=} )' ) 150 227 if nperio is None : 151 228 nperio = __guess_config__ (jpj, jpi, nperio=None, out=out) 229 PopStack ( f'__guess_nperio__ : {nperio}' ) 152 230 return nperio 153 231 154 def __guess_config__ (jpj , jpi, nperio=None, config=None, out='nperio') :232 def __guess_config__ (jpj:int, jpi:int, nperio:bool=None, config=None, out='nperio') : 155 233 '''Tries to guess the value of nperio (periodicity parameter). 156 234 … … 161 239 nperio : periodicity parameter 162 240 ''' 163 print ( jpi, jpj) 241 PushStack ( f'__guessConfig__ ( {jpj=}, {jpi=}, {nperio=}, {config=}, {out=}') 242 243 print (jpi, jpj) 164 244 if nperio is None : 165 245 ## Values for NEMO version < 4.2 … … 195 275 'nemo.py is not ready for this value' ) 196 276 197 if out == 'nperio' : 277 if out == 'nperio' : 278 PushStack ( f'__guessConfig__ : {nperio=}' ) 198 279 return nperio 199 if out == 'config' :280 if out == 'config' : 200 281 return config 201 if out == 'perio' : 282 PushStack ( f'__guessConfig__ : {config=}' ) 283 if out == 'perio' : 202 284 return iperio, jperio, nfold, nftype 285 PushStack ( f'__guessConfig__ : {iperio=}, {jperio=}, {nfold=}, {nftype=}' ) 203 286 if out in ['full', 'all'] : 204 return {'nperio':nperio, 'iperio':iperio, 'jperio':jperio, 'nfold':nfold, 'nftype':nftype} 287 zdict = {'nperio':nperio, 'iperio':iperio, 'jperio':jperio, 'nfold':nfold, 'nftype':nftype} 288 PushStack ( f'__guessConfig__ : {zdict}' ) 289 return zdict 205 290 206 291 def __guess_point__ (ptab) : … … 215 300 Credits : who is the original author ? 216 301 ''' 302 PushStack ( f'__guess_point__ ( ptab )') 217 303 218 304 gp = None 219 305 mmath = __mmath__ (ptab) 220 306 if mmath == xr : 221 if ('x_c' in ptab.dims and 'y_c' in ptab.dims ) : 222 gp = 'T' 223 if ('x_f' in ptab.dims and 'y_c' in ptab.dims ) : 224 gp = 'U' 225 if ('x_c' in ptab.dims and 'y_f' in ptab.dims ) : 226 gp = 'V' 227 if ('x_f' in ptab.dims and 'y_f' in ptab.dims ) : 228 gp = 'F' 229 if ('x_c' in ptab.dims and 'y_c' in ptab.dims 230 and 'z_c' in ptab.dims ) : 231 gp = 'T' 232 if ('x_c' in ptab.dims and 'y_c' in ptab.dims 233 and 'z_f' in ptab.dims ) : 234 gp = 'W' 235 if ('x_f' in ptab.dims and 'y_c' in ptab.dims 236 and 'z_f' in ptab.dims ) : 237 gp = 'U' 238 if ('x_c' in ptab.dims and 'y_f' in ptab.dims 239 and 'z_f' in ptab.dims ) : 240 gp = 'V' 241 if ('x_f' in ptab.dims and 'y_f' in ptab.dims 242 and 'z_f' in ptab.dims ) : 243 gp = 'F' 307 if ('x_c' in ptab.dims and 'y_c' in ptab.dims ) : gp = 'T' 308 if ('x_f' in ptab.dims and 'y_c' in ptab.dims ) : gp = 'U' 309 if ('x_c' in ptab.dims and 'y_f' in ptab.dims ) : gp = 'V' 310 if ('x_f' in ptab.dims and 'y_f' in ptab.dims ) : gp = 'F' 311 if ('x_c' in ptab.dims and 'y_c' in ptab.dims and 'z_c' in ptab.dims ) : gp = 'T' 312 if ('x_c' in ptab.dims and 'y_c' in ptab.dims and 'z_f' in ptab.dims ) : gp = 'W' 313 if ('x_f' in ptab.dims and 'y_c' in ptab.dims and 'z_f' in ptab.dims ) : gp = 'U' 314 if ('x_c' in ptab.dims and 'y_f' in ptab.dims and 'z_f' in ptab.dims ) : gp = 'V' 315 if ('x_f' in ptab.dims and 'y_f' in ptab.dims and 'z_f' in ptab.dims ) : gp = 'F' 244 316 245 317 if gp is None : 246 318 raise AttributeError ('in nemo module : cd_type not found, and cannot by guessed') 247 319 print ( f'Grid set as {gp} deduced from dims {ptab.dims}' ) 320 321 PopStack ( f'__guess_point__ : {gp}' ) 248 322 return gp 249 323 else : 324 PopStack ( f'__guess_point__ : cd_type not found, input is not an xarray data' ) 250 325 raise AttributeError ('in nemo module : cd_type not found, input is not an xarray data') 251 326 252 def get_shape ( ptab) :327 def get_shape (ptab) : 253 328 '''Get shape of ptab return a string with axes names 254 329 … … 258 333 etc ... 259 334 ''' 260 335 PushStack ( f'get_shape ( ptab )' ) 261 336 g_shape = '' 262 if __find_axis__ (ptab, 'x')[0] : 263 g_shape = 'X' 264 if __find_axis__ (ptab, 'y')[0] : 265 g_shape = 'Y' + g_shape 266 if __find_axis__ (ptab, 'z')[0] : 267 g_shape = 'Z' + g_shape 268 if __find_axis__ (ptab, 't')[0] : 269 g_shape = 'T' + g_shape 337 if __find_axis__ (ptab, 'x')[0] : g_shape = 'X' 338 if __find_axis__ (ptab, 'y')[0] : g_shape = 'Y' + g_shape 339 if __find_axis__ (ptab, 'z')[0] : g_shape = 'Z' + g_shape 340 if __find_axis__ (ptab, 't')[0] : g_shape = 'T' + g_shape 341 342 PopStack ( f'get_shape : {g_shape}' ) 270 343 return g_shape 271 344 272 def lbc_diag (nperio ) :345 def lbc_diag (nperio:int) : 273 346 '''Useful to switch between field with and without halo''' 347 PushStack ( f'lbc_diag ( {nperio=}' ) 274 348 lperio, aperio = nperio, False 275 if nperio == 4.2 : 276 lperio, aperio = 4, True277 if nperio == 6.2 : 278 lperio, aperio = 6, True349 if nperio == 4.2 : lperio, aperio = 4, True 350 if nperio == 6.2 : lperio, aperio = 6, True 351 352 PopStack ( f'lbc_diag : {lperio}, {aperio}' ) 279 353 return lperio, aperio 280 354 281 def __find_axis__ (ptab, axis='z', back =True) :355 def __find_axis__ (ptab, axis='z', back:bool=True, verbose:bool=False) : 282 356 '''Returns name and name of the requested axis''' 357 PushStack ( '__find_axis__ ( ptab, {axis=}, {back=}, {verbose=}' ) 358 283 359 mmath = __mmath__ (ptab) 284 360 ax, ix = None, None … … 286 362 if axis in XNAME : 287 363 ax_name, unit_list, length = XNAME, XUNIT, XLENGTH 364 if verbose : print ( f'Working on xaxis found by name : {axis=} : {XNAME=} {ax_name=} {unit_list=} {length=}' ) 288 365 if axis in YNAME : 289 366 ax_name, unit_list, length = YNAME, YUNIT, YLENGTH 367 if verbose : print ( f'Working on yaxis found by name : {axis=} : {YNAME=} {ax_name=} {unit_list=} {length=}' ) 290 368 if axis in ZNAME : 291 369 ax_name, unit_list, length = ZNAME, ZUNIT, ZLENGTH 370 if verbose : print ( f'Working on zaxis found by name : {axis=} : {ZNAME=} {ax_name=} {unit_list=} {length=}' ) 292 371 if axis in TNAME : 293 372 ax_name, unit_list, length = TNAME, TUNIT, None 294 295 if mmath == xr : 373 if verbose : print ( f'Working on taxis found by name : {axis=} : {TNAME=} {ax_name=} {unit_list=} {length=}' ) 374 375 if verbose : 376 print ( f'{ax_name=} {unit_list=} {length=}' ) 377 378 if mmath in [xr, 'dataset' ]: 296 379 # Try by name 297 for dim in ax_name : 298 if dim in ptab.dims : 299 ix, ax = ptab.dims.index (dim), dim 380 for i, dim in enumerate (ptab.dims) : 381 if verbose : print ( f'{i=} {dim=}' ) 382 if dim in ax_name : 383 if verbose : print ( f'Rule 2 : {dim=} axis found by name : {XNAME=}' ) 384 ix, ax = i, dim 300 385 301 386 # If not found, try by axis attributes 302 387 if not ix : 388 if verbose : print ( 'ix not found - 1' ) 303 389 for i, dim in enumerate (ptab.dims) : 390 if verbose : print ( f'{i=} {dim=}' ) 304 391 if 'axis' in ptab.coords[dim].attrs.keys() : 305 392 l_axis = ptab.coords[dim].attrs['axis'] 306 if axis in ax_name and l_axis == 'X' : 393 if verbose : print ( f'Rule 3 : Trying {i=} {dim=} {l_axis=}' ) 394 if l_axis in ax_name and l_axis == 'X' : 395 if verbose : print ( f'Rule 3 : xaxis found by name : {ax=} {l_axis=} {axis=} : {ax_name=} {l_axis=} {i=} {dim=}' ) 307 396 ix, ax = (i, dim) 308 if axis in ax_name and l_axis == 'Y' : 397 if l_axis in ax_name and l_axis == 'Y' : 398 if verbose : print ( f'Rule 3 : yaxis found by name : {ax=} {l_axis=} {axis=} : {ax_name=} {l_axis=} {i=} {dim=}' ) 309 399 ix, ax = (i, dim) 310 if axis in ax_name and l_axis == 'Z' : 400 if l_axis in ax_name and l_axis == 'Z' : 401 if verbose : print ( f'Rule 3 : zaxis found by name : {ax=} {l_axis=} {axis=} : {ax_name=} {l_axis=} {i=} {dim=}' ) 311 402 ix, ax = (i, dim) 312 if axis in ax_name and l_axis == 'T' : 403 if l_axis in ax_name and l_axis == 'T' : 404 if verbose : print ( f'Rule 3 : taxis found by name : {ax=} {l_axis=} {axis=} : {ax_name=} {l_axis=} {i=} {dim=}' ) 313 405 ix, ax = (i, dim) 314 406 … … 319 411 for name in unit_list : 320 412 if name in ptab.coords[dim].attrs['units'] : 413 if verbose : print ( f'Rule 4 : {name=} found by unit : {axis=} : {unit_list=} {i=} {dim=}' ) 321 414 ix, ax = i, dim 322 415 323 416 # If numpy array or dimension not found, try by length 324 if mmath != xr or not ix : 417 418 if mmath not in [xr, 'dataset'] or not ix : 325 419 if length : 326 l_shape = ptab.shape 420 if mmath in [xr, 'dataset'] : 421 l_shape =[ len (x) for x in ptab.dims ] 422 else : 423 l_shape = ptab.shape 327 424 for nn in np.arange ( len(l_shape) ) : 328 425 if l_shape[nn] in length : 329 ix = nn 426 ix = nn ; ax = None 427 if verbose : print ( f'Rule 5 : {ax_name=} axis found by length : {axis=} : {XNAME=} {ix=} {ax=}' ) 330 428 331 429 if ix and back : 332 ix -= len(ptab.shape) 333 430 if mmath in [xr, 'dataset'] : 431 ix -= len (ptab.dims) 432 else : 433 ix -= len (ptab.shape) 434 435 PopStack ( f'__find_axis__ : {ax}, {ix}' ) 334 436 return ax, ix 335 437 336 def find_axis ( ptab, axis='z', back=True) :438 def find_axis (ptab, axis:str='z', back:bool=True, verbose:bool=False) : 337 439 '''Version of find_axis with no __''' 338 ix, xx = __find_axis__ (ptab, axis, back) 440 PushStack ( f'find_axis ( ptab, {axis=}, {back=}, {varbose=}' ) 441 442 ix, xx = __find_axis__ (ptab, axis, back, verbose) 443 444 PopStack ( f'find_axis : {xx}, {ix}' ) 339 445 return xx, ix 340 446 341 def fixed_lon (plon, center_lon =0.0) :447 def fixed_lon (plon, center_lon:float=0.0) : 342 448 '''Returns corrected longitudes for nicer plots 343 449 … … 348 454 See https://gist.github.com/pelson/79cf31ef324774c97ae7 349 455 ''' 456 PushStack ( f'fixed_lon ( plon, {center_lon=}' ) 350 457 mmath = __mmath__ (plon) 351 458 … … 359 466 360 467 # Special case for eORCA025 361 if f_lon.shape [-1] == 1442 : 362 f_lon [..., -2, :] = f_lon [..., -3, :] 363 if f_lon.shape [-1] == 1440 : 364 f_lon [..., -1, :] = f_lon [..., -2, :] 365 366 if f_lon.min () > center_lon : 367 f_lon += -360.0 368 if f_lon.max () < center_lon : 369 f_lon += 360.0 370 371 if f_lon.min () < center_lon-360.0 : 372 f_lon += 360.0 373 if f_lon.max () > center_lon+360.0 : 374 f_lon += -360.0 375 468 if f_lon.shape [-1] == 1442 : f_lon [..., -2, :] = f_lon [..., -3, :] 469 if f_lon.shape [-1] == 1440 : f_lon [..., -1, :] = f_lon [..., -2, :] 470 471 if f_lon.min () > center_lon : f_lon += -360.0 472 if f_lon.max () < center_lon : f_lon += 360.0 473 474 if f_lon.min () < center_lon-360.0 : f_lon += 360.0 475 if f_lon.max () > center_lon+360.0 : f_lon += -360.0 476 477 PopStack ( f'fixed_lon : f_lon' ) 376 478 return f_lon 377 479 378 def bounds_clolon ( pbounds_lon, plon, rad=False, deg=True) :480 def bounds_clolon (pbounds_lon, plon, rad:bool=False, deg:bool=True) : 379 481 '''Choose closest to lon0 longitude, adding/substacting 360° if needed 380 482 ''' 381 382 if rad : 383 lon_range = 2.0*np.pi 384 if deg : 385 lon_range = 360.0 483 PushStack ( f'bounds_clolon ( pbounds_lon, plon, {rad=}, {deb=}' ) 484 if rad : lon_range = 2.0*np.pi 485 if deg : lon_range = 360.0 386 486 b_clolon = pbounds_lon.copy () 387 487 … … 392 492 b_clolon-lon_range, 393 493 b_clolon ) 494 495 PopStack ( f'bounds_clolon : b_clolon' ) 394 496 return b_clolon 395 497 396 def unify_dims ( dd , x='x', y='y', z='olevel', t='time_counter', verbose=False ):498 def unify_dims ( dd: "DataArray or Dataset", x=UDIMS['x'], y=UDIMS['y'], z=UDIMS['z'], t=UDIMS['t'], verbose:bool=False ) -> "DataArray or Dataset" : 397 499 '''Rename dimensions to unify them between NEMO versions 398 500 ''' 501 PushStack ( f'unify_dims ( dd, {x=}, {y=}, {z=}, {t=}, {verbose=}' ) 399 502 for xx in XNAME : 400 503 if xx in dd.dims and xx != x : 401 if verbose : 402 print ( f"{xx} renamed to {x}" ) 504 if verbose : print ( f"{xx} renamed to {x}" ) 403 505 dd = dd.rename ( {xx:x}) 404 506 405 507 for yy in YNAME : 406 508 if yy in dd.dims and yy != y : 407 if verbose : 408 print ( f"{yy} renamed to {y}" ) 509 if verbose : print ( f"{yy} renamed to {y}" ) 409 510 dd = dd.rename ( {yy:y} ) 410 511 411 512 for zz in ZNAME : 412 513 if zz in dd.dims and zz != z : 413 if verbose : 414 print ( f"{zz} renamed to {z}" ) 514 if verbose : print ( f"{zz} renamed to {z}" ) 415 515 dd = dd.rename ( {zz:z} ) 416 516 417 517 for tt in TNAME : 418 518 if tt in dd.dims and tt != t : 419 if verbose : 420 print ( f"{tt} renamed to {t}" ) 519 if verbose : print ( f"{tt} renamed to {t}" ) 421 520 dd = dd.rename ( {tt:t} ) 422 521 522 PopStack ( f'unify_dims : dd' ) 423 523 return dd 424 524 425 525 426 526 if SimpleImputer : 427 def fill_empty (ptab, sval=np.nan, transpose=False) :527 def fill_empty (ptab, sval:float=np.nan, transpose:bool=False) : 428 528 '''Fill empty values 429 529 … … 432 532 values 433 533 ''' 534 PushStack ( f'fill_empty ( ptab, {sval=} {transpose=}' ) 434 535 mmath = __mmath__ (ptab) 435 536 … … 446 547 ztab.attrs.update (ptab.attrs) 447 548 549 PopStack ( f'fill_empty : ztab' ) 448 550 return ztab 449 551 552 def fill_latlon (plat, plon, sval=-1) : 553 '''Fill latitude/longitude values 554 555 Useful when NEMO has run with no wet points options : 556 some parts of the domain, with no ocean points, have no 557 lon/lat values 558 ''' 559 PushStack ( f'fill_latlon ( plat, plon {sval=}' ) 560 561 from sklearn.impute import SimpleImputer 562 mmath = __mmath__ (plon) 563 564 imp = SimpleImputer (missing_values=sval, strategy='mean') 565 imp.fit (plon) 566 zlon = imp.transform (plon) 567 imp.fit (plat.T) 568 zlat = imp.transform (plat.T).T 569 570 if mmath == xr : 571 zlon = xr.DataArray (zlon, dims=plon.dims, coords=plon.coords) 572 zlat = xr.DataArray (zlat, dims=plat.dims, coords=plat.coords) 573 zlon.attrs.update (plon.attrs) 574 zlat.attrs.update (plat.attrs) 575 576 zlon = fixed_lon (zlon) 577 578 PopStack ( f'fixed_lon') 579 return zlat, zlon 580 581 def fill_lonlat (plon, plat, sval=-1) : 582 '''Fill longitude/latitude values 583 584 Useful when NEMO has run with no wet points options : 585 some parts of the domain, with no ocean points, have no 586 lon/lat values 587 ''' 588 PushStack ( f'fill_lonlat ( plon, plat {sval=}' ) 589 from sklearn.impute import SimpleImputer 590 mmath = __mmath__ (plon) 591 592 imp = SimpleImputer (missing_values=sval, strategy='mean') 593 imp.fit (plon) 594 zlon = imp.transform (plon) 595 imp.fit (plat.T) 596 zlat = imp.transform (plat.T).T 597 598 if mmath == xr : 599 zlon = xr.DataArray (zlon, dims=plon.dims, coords=plon.coords) 600 zlat = xr.DataArray (zlat, dims=plat.dims, coords=plat.coords) 601 zlon.attrs.update (plon.attrs) 602 zlat.attrs.update (plat.attrs) 603 604 zlon = fixed_lon (zlon) 605 606 PopStack ( f'fill_lonlat' ) 607 return zlon, zlat 608 609 def fill_bounds_lonlat (pbounds_lon, pbounds_lat, sval=-1) : 610 '''Fill longitude/latitude bounds values 611 612 Useful when NEMO has run with no wet points options : 613 some parts of the domain, with no ocean points, as no 614 lon/lat values 615 ''' 616 PushStack ( f'fill_bounds_lonlat (pbounds_lon, pbounds_lat, {sval=}' ) 617 mmath = __mmath__ (pbounds_lon) 618 619 z_bounds_lon = np.empty_like (pbounds_lon) 620 z_bounds_lat = np.empty_like (pbounds_lat) 621 622 imp = SimpleImputer (missing_values=sval, strategy='mean') 623 624 for n in np.arange (4) : 625 imp.fit (pbounds_lon[:,:,n]) 626 z_bounds_lon[:,:,n] = imp.transform (pbounds_lon[:,:,n]) 627 imp.fit (pbounds_lat[:,:,n].T) 628 z_bounds_lat[:,:,n] = imp.transform (pbounds_lat[:,:,n].T).T 629 630 if mmath == xr : 631 z_bounds_lon = xr.DataArray (pbounds_lon, dims=pbounds_lon.dims, 632 coords=pbounds_lon.coords) 633 z_bounds_lat = xr.DataArray (pbounds_lat, dims=pbounds_lat.dims, 634 coords=pbounds_lat.coords) 635 z_bounds_lon.attrs.update (pbounds_lat.attrs) 636 z_bounds_lat.attrs.update (pbounds_lat.attrs) 637 638 PopStack ( 'fill_bounds_lonlat' ) 639 return z_bounds_lon, z_bounds_lat 450 640 451 641 else : … … 461 651 values 462 652 ''' 653 PushStack ( f'fill_empty [void version] ( ptab, {sval=} {transpose=}' ) 654 463 655 print ( 'Error : module sklearn.impute.SimpleImputer not found' ) 464 656 print ( 'Can not call fill_empty' ) 465 657 print ( 'Call arguments where : ' ) 466 658 print ( f'{ptab.shape=} {sval=} {transpose=}' ) 467 468 def fill_lonlat (plon, plat, sval=-1) : 469 '''Fill longitude/latitude values 470 471 Useful when NEMO has run with no wet points options : 472 some parts of the domain, with no ocean points, have no 473 lon/lat values 474 ''' 475 from sklearn.impute import SimpleImputer 476 mmath = __mmath__ (plon) 477 478 imp = SimpleImputer (missing_values=sval, strategy='mean') 479 imp.fit (plon) 480 zlon = imp.transform (plon) 481 imp.fit (plat.T) 482 zlat = imp.transform (plat.T).T 483 484 if mmath == xr : 485 zlon = xr.DataArray (zlon, dims=plon.dims, coords=plon.coords) 486 zlat = xr.DataArray (zlat, dims=plat.dims, coords=plat.coords) 487 zlon.attrs.update (plon.attrs) 488 zlat.attrs.update (plat.attrs) 489 490 zlon = fixed_lon (zlon) 491 492 return zlon, zlat 493 494 def fill_bounds_lonlat (pbounds_lon, pbounds_lat, sval=-1) : 495 '''Fill longitude/latitude bounds values 496 497 Useful when NEMO has run with no wet points options : 498 some parts of the domain, with no ocean points, as no 499 lon/lat values 500 ''' 501 mmath = __mmath__ (pbounds_lon) 502 503 z_bounds_lon = np.empty ( pbounds_lon.shape ) 504 z_bounds_lat = np.empty ( pbounds_lat.shape ) 505 506 imp = SimpleImputer (missing_values=sval, strategy='mean') 507 508 for n in np.arange (4) : 509 imp.fit (pbounds_lon[:,:,n]) 510 z_bounds_lon[:,:,n] = imp.transform (pbounds_lon[:,:,n]) 511 imp.fit (pbounds_lat[:,:,n].T) 512 z_bounds_lat[:,:,n] = imp.transform (pbounds_lat[:,:,n].T).T 513 514 if mmath == xr : 515 z_bounds_lon = xr.DataArray (pbounds_lon, dims=pbounds_lon.dims, 516 coords=pbounds_lon.coords) 517 z_bounds_lat = xr.DataArray (pbounds_lat, dims=pbounds_lat.dims, 518 coords=pbounds_lat.coords) 519 z_bounds_lon.attrs.update (pbounds_lat.attrs) 520 z_bounds_lat.attrs.update (pbounds_lat.attrs) 521 522 return z_bounds_lon, z_bounds_lat 659 PopStack ( f'fill_empty [void version]' ) 660 661 662 def fill_latlon (plat, plon, sval=-1) : 663 ''' Void version of fill_latlon, because module sklearn.impute.SimpleImputer is not available 664 665 Useful when NEMO has run with no wet points options : 666 some parts of the domain, with no ocean points, have no 667 lon/lat values 668 ''' 669 PushStack ( f'fill_latlon [void_version] ( plat, plon {sval=}' ) 670 671 print ( 'Error : module sklearn.impute.SimpleImputer not found' ) 672 print ( 'Can not call fill_empty' ) 673 print ( 'Call arguments where : ' ) 674 print ( f'{ptab.shape=} {sval=} {transpose=}' ) 675 676 PopStack ( f'fixed_lon [void version]') 677 678 def fill_lonlat (plon, plat, sval=-1) : 679 ''''Void version of fill_lonlat, because module sklearn.impute.SimpleImputer is not available 680 681 Useful when NEMO has run with no wet points options : 682 some parts of the domain, with no ocean points, have no 683 lon/lat values 684 ''' 685 PushStack ( f'fill_lonlat [void Version] ( plon, plat {sval=}' ) 686 687 print ( 'Error : module sklearn.impute.SimpleImputer not found' ) 688 print ( 'Can not call fill_empty' ) 689 print ( 'Call arguments where : ' ) 690 print ( f'{ptab.shape=} {sval=} {transpose=}' ) 691 692 PopStack ( f'fill_lonlat [void version]' ) 693 694 def fill_bounds_lonlat (pbounds_lon, pbounds_lat, sval=-1) : 695 ''''Void version of fill_bounds_lonlat, because module sklearn.impute.SimpleImputer is not available 696 697 Useful when NEMO has run with no wet points options : 698 some parts of the domain, with no ocean points, as no 699 lon/lat values 700 ''' 701 PushStack ( f'fill_bounds_lonlat [void version] (pbounds_lon, pbounds_lat, {sval=}' ) 702 703 print ( 'Error : module sklearn.impute.SimpleImputer not found' ) 704 print ( 'Can not call fill_empty' ) 705 print ( 'Call arguments where : ' ) 706 print ( f'{ptab.shape=} {sval=} {transpose=}' ) 707 708 PopStack ( 'fill_bounds_lonlat [void version]' ) 523 709 524 710 def jeq (plat) : … … 527 713 lat : latitudes of the grid. At least 2D. 528 714 ''' 715 PushStack ( f'jeq ( plat ) ' ) 529 716 mmath = __mmath__ (plat) 530 717 jy = __find_axis__ (plat, 'y')[-1] 531 718 532 if mmath == xr : 533 jj = int ( np.mean ( np.argmin (np.abs (np.float64 (plat)), 534 axis=jy) ) ) 535 else : 536 jj = np.argmin (np.abs (np.float64 (plat[...,:, 0]))) 537 719 if mmath == xr : jj = int ( np.mean ( np.argmin (np.abs (np.float64 (plat)), axis=jy) ) ) 720 else : jj = np.argmin (np.abs (np.float64 (plat[...,:, 0]))) 721 722 PopStack ( f'jeq : {jj}' ) 538 723 return jj 539 724 … … 544 729 plat (optionnal) : latitudes of the grid 545 730 ''' 731 PushStack ( f'lon1d ( plon, plat) ' ) 546 732 mmath = __mmath__ (plon) 547 733 jpj, jpi = plon.shape [-2:] … … 566 752 lon_1d.attrs['long_name :'] = 'Longitude' 567 753 754 PopStack ( 'lon_1d' ) 568 755 return lon_1d 569 756 … … 575 762 diff [optional] : tolerance 576 763 ''' 764 PushStack ( f'latreg ( plat, {diff=}' ) 577 765 #mmath = __mmath__ (plat) 578 766 if diff is None : … … 587 775 lareg = np.float64 (plat[...,jreg,:].mean(axis=-1)) 588 776 777 PopStack ( f'latreg : {jreg}, lareg' ) 589 778 return jreg, lareg 590 779 … … 594 783 plat : latitudes of the grid (2D) 595 784 ''' 785 PushStack ( f'lat1d ( plat )' ) 596 786 mmath = __mmath__ (plat) 597 787 iy = __find_axis__ (plat, 'y')[-1] … … 606 796 607 797 if np.abs (lat_eq) < dy/100. : # T, U or W grid 608 if jpj-1 > jreg : 609 dys = (90.-lat_reg) / (jpj-jreg-1)*0.5 610 else : 611 dys = (90.-lat_reg) / 2.0 798 if jpj-1 > jreg : dys = (90.-lat_reg) / (jpj-jreg-1)*0.5 799 else : dys = (90.-lat_reg) / 2.0 612 800 yrange = 90.-dys-lat_reg 613 else : # V or F grid 614 yrange = 90.-lat_reg 801 else : yrange = 90.-lat_reg # V or F grid 615 802 616 803 if jpj-1 > jreg : … … 629 816 lat_1d.attrs ['long_name :'] = 'Latitude' 630 817 818 PopStack ( f'lat1d' ) 631 819 return lat_1d 632 820 … … 636 824 plat, plon : latitudes and longitudes of the grid (2D) 637 825 ''' 638 return lat1d (plat), lon1d (plon, plat) 826 PushStack ( f'latlon1d ( plat, plon) ' ) 827 zla = lat1d (plat) 828 zlo = lon1d (plon, plat) 829 830 PopStack ( f'latlon1d' ) 831 return zla, zlo 639 832 640 833 def ff (plat) : 641 834 '''Returns Coriolis factor 642 835 ''' 643 zff = np.sin (RAD * plat) * OMEGA 836 PushStack ( 'ff ( plat )' ) 837 zff = np.sin (RAD * plat) * ROMEGA 838 PopStack ( 'ff' ) 644 839 return zff 645 840 … … 647 842 '''Return Beta factor (derivative of Coriolis factor) 648 843 ''' 649 zbeta = np.cos (RAD * plat) * OMEGA / RA 844 PushStack ( 'beta ( plat )' ) 845 zbeta = np.cos (RAD * plat) * ROMEGA / RA 846 PopStack ( 'beta' ) 650 847 return zbeta 651 848 … … 653 850 '''Returns masked values outside a lat/lon box 654 851 ''' 852 PushStack ( f'mask_lonlat (ptab, {x0=}, {x1=}, {y0=}, {y1=}, lon, lat, {sval=}' ) 655 853 mmath = __mmath__ (ptab) 656 854 if mmath == xr : … … 660 858 mask = np.logical_and (np.logical_and(lat>y0, lat<y1), 661 859 np.logical_or (np.logical_or ( 662 np.logical_and (lon>x0, lon<x1),663 np.logical_and (lon+360>x0, lon+360<x1)),664 np.logical_and (lon-360>x0, lon-360<x1)))860 np.logical_and (lon>x0, lon<x1), 861 np.logical_and (lon+360>x0, lon+360<x1)), 862 np.logical_and (lon-360>x0, lon-360<x1))) 665 863 tab = mmath.where (mask, ptab, sval) 666 864 865 PopStack ( 'mask_lonlat' ) 667 866 return tab 668 867 … … 684 883 685 884 ''' 885 PushStack ( f'extend ( ptab, {blon=}, {jplus=}, {jpi=}, {nperio=}' ) 686 886 mmath = __mmath__ (ptab) 687 887 688 if ptab.shape[-1] == 1 : 689 tabex = ptab 888 if ptab.shape[-1] == 1 : tabex = ptab 690 889 691 890 else : 692 if jpi is None : 693 jpi = ptab.shape[-1] 694 695 if blon : 696 xplus = -360.0 697 else : 698 xplus = 0.0 891 if not jpi : jpi = ptab.shape[-1] 892 893 if blon : xplus = -360.0 894 else : xplus = 0.0 699 895 700 896 if ptab.shape[-1] > jpi : 701 897 tabex = ptab 702 898 else : 703 if nperio in [ 0, 4.2 ] : 704 istart, le, la = 0, jpi+1, 0 705 if nperio == 1 : 706 istart, le, la = 0, jpi+1, 0 899 if nperio in [ 0, 4.2 ] : istart, le, la = 0, jpi+1, 0 900 if nperio == 1 : istart, le, la = 0, jpi+1, 0 707 901 if nperio in [4, 6] : # OPA case with two halo points for periodicity 708 902 # Perfect, except at the pole that should be masked by lbc_plot … … 716 910 new_coords = [] 717 911 for coord in ptab.dims : 718 if coord == lon : 719 new_coords.append ( np.arange( tabex.shape[-1])) 720 else : 721 new_coords.append ( ptab.coords[coord].values) 912 if coord == lon : new_coords.append ( np.arange( tabex.shape[-1])) 913 else : new_coords.append ( ptab.coords[coord].values) 722 914 tabex = xr.DataArray ( tabex, dims=ptab.dims, 723 915 coords=new_coords ) … … 727 919 ptab [..., istart+la:istart+la+jplus] ), 728 920 axis=-1) 921 922 PopStack ( 'extend' ) 729 923 return tabex 730 924 731 def orca2reg (dd, lat_name='nav_lat', lon_name='nav_lon', 732 y_name='y', x_name='x') : 925 def orca2reg (dd, lat_name=None, lon_name=None, y_name=None, x_name=None) : 733 926 '''Assign an ORCA dataset on a regular grid. 734 927 … … 741 934 Returns : xarray dataset with rectangular grid. Incorrect above 20°N 742 935 ''' 936 PushStack ( f'orca2reg ( dd, {lat_name=}, {lon_name=}, {y_name=}, {x_name=}' ) 937 if not x_name : x_name, ix = __find_axis__ (dd, axis='x') 938 if not y_name : y_name, jy = __find_axis__ (dd, axis='y') 939 940 if not lon_name : 941 for xn in LONNAME : 942 if xn in dd.variables : lon_name = xn 943 if not lat_name : 944 for yn in LATNAME : 945 if yn in dd.variables : lat_name = yn 946 947 print ( f'{dd.dims=}' ) 948 print ( f'{x_name=} {y_name=}' ) 949 743 950 # Compute 1D longitude and latitude 744 (zlat, zlon) = latlon1d ( dd[lat_name], dd[lon_name])745 951 ylat, xlon = fill_latlon (dd[lat_name], dd[lon_name], sval=-1) 952 (zlat, zlon) = latlon1d (ylat, xlon) 746 953 zdd = dd 954 747 955 # Assign lon and lat as dimensions of the dataset 748 956 if y_name in zdd.dims : … … 754 962 # Force dimensions to be in the right order 755 963 coord_order = ['lat', 'lon'] 756 for dim in [ ' depthw', 'depthv', 'depthu', 'deptht', 'depth', 'z',964 for dim in [ 'olevel', 'depthw', 'depthv', 'depthu', 'deptht', 'depth', 'z', 757 965 'time_counter', 'time', 'tbnds', 758 'bnds', 'axis_nbounds', 'two2', 'two1', 'two', 'four',] : 759 if dim in zdd.dims : 760 coord_order.insert (0, dim) 966 'bnds', 'axis_nbounds', 'nvertex', 'two2', 'two1', 'two', 'four',] : 967 if dim in zdd.dims : coord_order.insert (0, dim) 761 968 762 969 zdd = zdd.transpose (*coord_order) 970 971 PopStack ( 'orca2reg' ) 763 972 return zdd 764 973 … … 777 986 See NEMO documentation for further details 778 987 ''' 988 PushStack ( f'lbc_init ( ptab, {nperio=}' ) 779 989 jpi, jpj = None, None 780 990 ax, ix = __find_axis__ (ptab, 'x') 781 991 ay, jy = __find_axis__ (ptab, 'y') 782 if ax : 783 jpi = ptab.shape[ix] 784 if ay : 785 jpj = ptab.shape[jy] 786 787 if nperio is None : 788 nperio = __guess_nperio__ (jpj, jpi, nperio) 992 if ax : jpi = ptab.shape[ix] 993 if ay : jpj = ptab.shape[jy] 994 995 if nperio is None : nperio = __guess_nperio__ (jpj, jpi, nperio) 789 996 790 997 if nperio not in NPERIO_VALID_RANGE : 791 998 raise AttributeError ( f'{nperio=} is not in the valid range {NPERIO_VALID_RANGE}' ) 792 999 1000 PopStack ( f'lbc_init : {jpj}, {jpi}, {nperio}' ) 793 1001 return jpj, jpi, nperio 794 1002 … … 803 1011 See NEMO documentation for further details 804 1012 ''' 1013 PushStack ( f'lbc_init ( ptab, {nperio=}, {cd_type=}, {psgn=}, {nemo_4u_bug=}' ) 1014 805 1015 jpi, nperio = lbc_init (ptab, nperio)[1:] 806 ax = __find_axis__ (ptab, 'x')[0]807 ay = __find_axis__ (ptab, 'y')[0]1016 ax = __find_axis__ (ptab, 'x')[0] 1017 ay = __find_axis__ (ptab, 'y')[0] 808 1018 psgn = ptab.dtype.type (psgn) 809 1019 mmath = __mmath__ (ptab) 810 1020 811 if mmath == xr : 812 ztab = ptab.values.copy () 813 else : 814 ztab = ptab.copy () 1021 if mmath == xr : ztab = ptab.values.copy () 1022 else : ztab = ptab.copy () 815 1023 816 1024 if ax : … … 858 1066 if cd_type in [ 'T', 'W' ] : # T-, W-point 859 1067 ztab [..., -1, jpi//2: ] = psgn * ztab [..., -1, jpi//2:0:-1 ] 860 861 if cd_type == 'U' : 862 ztab [..., -1, jpi//2-1:-1] = psgn * ztab [..., -1, jpi//2:0:-1] 863 864 if cd_type == 'V' : 865 ztab [..., -1, 1: ] = psgn * ztab [..., -2, jpi-1:0:-1 ] 866 867 if cd_type == 'F' : 868 ztab [..., -1, 0:-1 ] = psgn * ztab [..., -2, -1:0:-1 ] 1068 if cd_type == 'U' : ztab [..., -1, jpi//2-1:-1] = psgn * ztab [..., -1, jpi//2:0:-1] 1069 if cd_type == 'V' : ztab [..., -1, 1: ] = psgn * ztab [..., -2, jpi-1:0:-1 ] 1070 if cd_type == 'F' : ztab [..., -1, 0:-1 ] = psgn * ztab [..., -2, -1:0:-1 ] 869 1071 870 1072 if nperio in [5, 6] : # North fold F-point pivot … … 897 1099 ztab.attrs = ptab.attrs 898 1100 1101 PopStack ( 'lbc' ) 899 1102 return ztab 900 1103 … … 908 1111 See NEMO documentation for further details 909 1112 ''' 1113 PushStack ( f'lbc_mask (ptab, {nperio=}, {cd_type=}, {sval=}' ) 910 1114 jpi, nperio = lbc_init (ptab, nperio)[1:] 911 1115 ax = __find_axis__ (ptab, 'x')[0] … … 926 1130 #> South (in which nperio cases ?) 927 1131 # -------------------------------- 928 if nperio in [1, 3, 4, 5, 6] : 929 ztab [..., 0, :] = sval 1132 if nperio in [1, 3, 4, 5, 6] : ztab [..., 0, :] = sval 930 1133 931 1134 # … … 952 1155 if cd_type in [ 'T', 'W' ] : # T-, W-point 953 1156 ztab [..., -1, jpi//2 : ] = sval 954 955 if cd_type == 'U' : 956 ztab [..., -1, jpi//2-1:-1] = sval 957 958 if cd_type == 'V' : 959 ztab [..., -1, 1: ] = sval 960 961 if cd_type == 'F' : 962 ztab [..., -1, 0:-1 ] = sval 1157 if cd_type == 'U' : ztab [..., -1, jpi//2-1:-1] = sval 1158 if cd_type == 'V' : ztab [..., -1, 1: ] = sval 1159 if cd_type == 'F' : ztab [..., -1, 0:-1 ] = sval 963 1160 964 1161 if nperio in [5, 6] : # North fold F-point pivot … … 979 1176 ztab [..., -2, jpi//2+1:-1] = sval 980 1177 1178 PopStack ( 'lbc_mask' ) 981 1179 return ztab 982 1180 … … 994 1192 See NEMO documentation for further details 995 1193 ''' 1194 PushStack ( f'lbc_plot (ptab, {nperio=}, {cd_type=}, {psgn=}, {sval=}' ) 996 1195 jpi, nperio = lbc_init (ptab, nperio)[1:] 997 1196 ax = __find_axis__ (ptab, 'x')[0] … … 1012 1211 #> Masks south 1013 1212 # ------------ 1014 if nperio in [4, 6] : 1015 ztab [..., 0, : ] = sval 1213 if nperio in [4, 6] : ztab [..., 0, : ] = sval 1016 1214 1017 1215 # … … 1037 1235 if cd_type in [ 'T', 'W' ] : # T-, W-point 1038 1236 ztab [..., -1, jpi//2: ] = sval 1039 1040 if cd_type == 'U' : 1041 ztab [..., -1, jpi//2-1:-1] = sval 1042 1043 if cd_type == 'V' : 1044 ztab [..., -1, 1: ] = sval 1045 1046 if cd_type == 'F' : 1047 ztab [..., -1, 0:-1 ] = sval 1237 if cd_type == 'U' : ztab [..., -1, jpi//2-1:-1] = sval 1238 if cd_type == 'V' : ztab [..., -1, 1: ] = sval 1239 if cd_type == 'F' : ztab [..., -1, 0:-1 ] = sval 1048 1240 1049 1241 if nperio in [5, 6] : # North fold F-point pivot … … 1062 1254 ztab [..., -2, jpi//2+1:-1] = sval 1063 1255 1256 PopStack ( 'lbc_plot' ) 1064 1257 return ztab 1065 1258 1066 1259 def lbc_add (ptab, nperio=None, cd_type=None, psgn=1) : 1067 '''Handles NEMO domain changes between NEMO 4.0 to NEMO 4.2 1260 ''' 1261 Handles NEMO domain changes between NEMO 4.0 to NEMO 4.2 1068 1262 1069 1263 Periodicity and north fold halos has been removed in NEMO 4.2 … … 1076 1270 See NEMO documentation for further details 1077 1271 ''' 1272 PushStack ( f'lbc_add ( ptab, {nperio=}, {cd_type=}, {psgn=} )' ) 1078 1273 mmath = __mmath__ (ptab) 1079 1274 nperio = lbc_init (ptab, nperio)[-1] … … 1097 1292 ptab_ext.values[..., :-1, 1:-1] = ptab.values.copy () 1098 1293 else : 1099 if 'X' in lshape : 1100 ptab_ext.values[..., 1:-1] = ptab.values.copy () 1101 if 'Y' in lshape : 1102 ptab_ext.values[..., :-1 ] = ptab.values.copy () 1294 if 'X' in lshape : ptab_ext.values[..., 1:-1] = ptab.values.copy () 1295 if 'Y' in lshape : ptab_ext.values[..., :-1 ] = ptab.values.copy () 1103 1296 else : 1104 1297 ptab_ext = np.zeros (ext_shape) … … 1106 1299 ptab_ext [..., :-1, 1:-1] = ptab.copy () 1107 1300 else : 1108 if 'X' in lshape : 1109 ptab_ext [..., 1:-1] = ptab.copy () 1110 if 'Y' in lshape : 1111 ptab_ext [..., :-1 ] = ptab.copy () 1301 if 'X' in lshape : ptab_ext [..., 1:-1] = ptab.copy () 1302 if 'Y' in lshape : ptab_ext [..., :-1 ] = ptab.copy () 1112 1303 1113 1304 if nperio == 4.2 : … … 1120 1311 az = __find_axis__ (ptab, 'z')[0] 1121 1312 at = __find_axis__ (ptab, 't')[0] 1122 if az : 1123 ptab_ext = ptab_ext.assign_coords ( {az:ptab.coords[az]} ) 1124 if at : 1125 ptab_ext = ptab_ext.assign_coords ( {at:ptab.coords[at]} ) 1313 if az : ptab_ext = ptab_ext.assign_coords ( {az:ptab.coords[az]} ) 1314 if at : ptab_ext = ptab_ext.assign_coords ( {at:ptab.coords[at]} ) 1126 1315 1127 1316 else : ptab_ext = lbc (ptab, nperio=nperio, cd_type=cd_type, psgn=psgn) 1128 1317 1318 PopStack ( 'lbc_add' ) 1129 1319 return ptab_ext 1130 1320 … … 1141 1331 See NEMO documentation for further details 1142 1332 ''' 1333 PushStack ( f'lbc_del (ptab, {nperio=}, {cd_type=}, {psgn=}' ) 1143 1334 nperio = lbc_init (ptab, nperio)[-1] 1144 1335 #lshape = get_shape (ptab) … … 1148 1339 if nperio in [4.2, 6.2] : 1149 1340 if ax or ay : 1150 if ax and ay : 1151 ztab = lbc (ptab[..., :-1, 1:-1], 1152 nperio=nperio, cd_type=cd_type, psgn=psgn) 1341 if ax and ay : ztab = lbc (ptab[..., :-1, 1:-1], nperio=nperio, cd_type=cd_type, psgn=psgn) 1153 1342 else : 1154 if ax : 1155 ztab = lbc (ptab[..., 1:-1], 1156 nperio=nperio, cd_type=cd_type, psgn=psgn) 1157 if ay : 1158 ztab = lbc (ptab[..., -1], 1159 nperio=nperio, cd_type=cd_type, psgn=psgn) 1160 else : 1161 ztab = ptab 1162 else : 1163 ztab = ptab 1164 1343 if ax : ztab = lbc (ptab[..., 1:-1], nperio=nperio, cd_type=cd_type, psgn=psgn) 1344 if ay : ztab = lbc (ptab[..., -1] , nperio=nperio, cd_type=cd_type, psgn=psgn) 1345 else : ztab = ptab 1346 else : ztab = ptab 1347 1348 PopStack ( 'lbc_del' ) 1165 1349 return ztab 1166 1350 … … 1176 1360 See NEMO documentation for further details 1177 1361 ''' 1178 1362 PushStack ( f'lbc_index ( {jj=}, {ii=}, {jpj=}, {jpi=}, {nperio=}, {cd_type=} )' ) 1179 1363 if nperio is None : 1180 1364 nperio = __guess_nperio__ (jpj, jpi, nperio) … … 1186 1370 1187 1371 mmath = __mmath__ (jj) 1188 if mmath is None : 1189 mmath=np 1372 if not mmath : mmath=np 1190 1373 1191 1374 # … … 1199 1382 # 1200 1383 def mod_ij (cond, jy_new, ix_new) : 1384 PushStack ( f'mod_ij (cond, jy_new, ix_new)' ) 1201 1385 jy_r = mmath.where (cond, jy_new, jy) 1202 1386 ix_r = mmath.where (cond, ix_new, ix) 1387 PopStack ( 'mod_ij' ) 1203 1388 return jy_r, ix_r 1204 1389 # … … 1251 1436 1252 1437 ## Restore convention to Python/C : indexes start at 0 1253 jy += -1 1254 ix += -1 1255 1256 if isinstance (jj, int) : 1257 jy = jy.item () 1258 if isinstance (ii, int) : 1259 ix = ix.item () 1260 1438 jy += -1 ; ix += -1 1439 1440 if isinstance (jj, int) : jy = jy.item () 1441 if isinstance (ii, int) : ix = ix.item () 1442 1443 PopStack ( f'lbc_index = {jy}, {ix}' ) 1261 1444 return jy, ix 1262 1445 1263 def find_ji (lat_data, lon_data, lat_grid, lon_grid, mask=1.0, verbose=False, out=None) :1446 def find_ji (lat_data, lon_data, lat_grid, lon_grid, mask=1.0, verbose=False, drop_duplicates=False, out=None) : 1264 1447 ''' 1265 1448 Description: seeks J,I indices of the grid point which is the closest 1266 1449 of a given point 1267 1450 1268 Usage: go FindJI<data latitude> <data longitude> <grid latitudes> <grid longitudes> [mask]1451 Usage: go find_ji <data latitude> <data longitude> <grid latitudes> <grid longitudes> [mask] 1269 1452 <grid latitudes><grid longitudes> are 2D fields on J/I (Y/X) dimensions 1270 1453 mask : if given, seek only non masked grid points (i.e with mask=1) 1271 1454 1272 Example : find IJ (40, -20, nav_lat, nav_lon, mask=1.0)1455 Example : find_ji (40., -20., nav_lat, nav_lon, mask=1.0) 1273 1456 1274 1457 Note : all longitudes and latitudes in degrees 1275 1458 1276 Note : may work with 1D lon/lat (?) 1277 ''' 1459 Note : may work with 1D lon_data/lat_data (?) 1460 ''' 1461 PushStack ( f'find_ji ( lat_data, lon_data, lat_grid, lon_grid, mask, {verbose=}, {drop_duplicates=}, {out=} ) ') 1278 1462 # Get grid dimensions 1279 if len (lon_grid.shape) == 2 : 1280 jpi = lon_grid.shape[-1] 1281 else : 1282 jpi = len(lon_grid) 1463 if len (lon_grid.shape) == 2 : jpi = lon_grid.shape[-1] 1464 else : jpi = len(lon_grid) 1283 1465 1284 1466 #mmath = __mmath__ (lat_grid) 1467 1468 if verbose : 1469 print ( f'{lat_data=}' ) 1470 print ( f'{lon_data=}' ) 1285 1471 1286 1472 # Compute distance from the point to all grid points (in RADian) … … 1288 1474 + np.cos (RAD*lat_data) * np.cos (RAD*lat_grid) * 1289 1475 np.cos(RAD*(lon_data-lon_grid)) ) 1476 1477 if verbose : print ( f'{type(arg)=} {arg.shape=}' ) 1478 1290 1479 # Send masked points to 'infinite' 1291 1480 distance = np.arccos (arg) + 4.0*RPI*(1.0-mask) 1292 1481 1293 # Truncates to alleviate some precision problem with some grids 1482 if verbose : print ( f'{type(distance)=} {distance.shape=}' ) 1483 1484 # Truncates to alleviate precision problem encountered with some grids 1294 1485 prec = int (1E7) 1295 1486 distance = (distance*prec).astype(int) / prec 1296 1297 # Compute minimum of distance, and index of minimum 1298 # 1299 #distance_min = distance.min () 1300 jimin = int (distance.argmin ()) 1487 if verbose : print ( f'{type(distance)=} {distance.shape=}' ) 1488 1489 # Compute index minimum of distance 1490 try : 1491 nn = len (lat_data) 1492 except : 1493 nn = 0 1494 jimin = distance.argmin ().astype(int) 1495 else : 1496 jimin = np.empty (nn, dtype=int ) 1497 for ji in np.arange (nn) : 1498 jimin[ji] = distance[ji].argmin() 1499 finally : 1500 if verbose : 1501 print ( f'{type(jimin)=} {jimin.shape}' ) 1502 print ( f'{jimin=}' ) 1301 1503 1302 1504 # Compute 2D indices (Python/C flavor : starting at 0) … … 1304 1506 imin = jimin - jmin*jpi 1305 1507 1306 # Result1307 1508 if verbose : 1308 # Compute distance achieved 1309 #mindist = distance [jmin, imin] 1310 1311 # Compute azimuth 1312 dlon = lon_data-lon_grid[jmin,imin] 1313 arg = np.sin (RAD*dlon) / ( 1314 np.cos(RAD*lat_data)*np.tan(RAD*lat_grid[jmin,imin]) 1315 - np.sin(RAD*lat_data)*np.cos(RAD*dlon)) 1316 azimuth = DAR*np.arctan (arg) 1317 print ( f'I={imin:d} J={jmin:d} - Data:{lat_data:5.1f}°N {lon_data:5.1f}°E' \ 1318 + f'- Grid:{lat_grid[jmin,imin]:4.1f}°N ' \ 1319 + f'{lon_grid[jmin,imin]:4.1f}°E - Dist: {RA*distance[jmin,imin]:6.1f}km' \ 1320 + f' {DAR*distance[jmin,imin]:5.2f}° ' \ 1321 + f'- Azimuth: {RAD*azimuth:3.2f}RAD - {azimuth:5.1f}°' ) 1322 1509 print ( f'{jmin=}' ) 1510 print ( f'{imin=}' ) 1511 1512 if drop_duplicates : 1513 zz = np.vstack ( (np.array (jmin), np.array (imin)) ) 1514 zz = np.swapaxes (zz , 0, 1) 1515 zz = np.unique ( zz, axis=0) 1516 jmin = zz[:,-2] 1517 imin = zz[:,-1] 1518 1519 PopStack ( 'find_ji' ) 1323 1520 if out=='dict' : 1324 return {'x':imin, 'y':jmin} 1521 zdict = {UDIMS['y']:jmin, UDIMS['x']:imin} 1522 return zdict 1325 1523 elif out in ['array', 'numpy', 'np'] : 1326 return np.array ( [jmin, imin])1524 return np.array (jmin), np.array (imin) 1327 1525 elif out in ['xarray', 'xr'] : 1328 return xr.DataArray ( [jmin, imin] ) 1526 jmin = xr.DataArray (jmin, dims=('Num',)) 1527 imin = xr.DataArray (imin, dims=('Num',)) 1528 jmin.attrs.update ( {'long_name':'j-index' } ) 1529 imin.attrs.update ( {'long_name':'i-index' } ) 1530 jmin.name = 'j_index' 1531 imin.name = 'j_index' 1532 return jmin, imin 1329 1533 elif out=='list' : 1330 1534 return [jmin, imin] … … 1337 1541 '''Returns curl of a vector field 1338 1542 ''' 1543 PushStack ( f'curl ( tx, ty, e1f, e2f, {nperio=} )' ) 1339 1544 ax = __find_axis__ (tx, 'x')[0] 1340 1545 ay = __find_axis__ (ty, 'y')[0] … … 1361 1566 zcurl = lbc (zcurl, nperio=nperio, cd_type='F', psgn=1) 1362 1567 1568 PopStack ( 'curl' ) 1363 1569 return zcurl 1364 1570 … … 1366 1572 '''Returns divergence of a vector field 1367 1573 ''' 1574 PushStack ( f'div (ux, uy, e1t, e2t, {nperio=}' ) 1368 1575 ax = __find_axis__ (ux, 'x')[0] 1369 1576 ay = __find_axis__ (ux, 'y')[0] … … 1390 1597 zdiv = lbc (zdiv, nperio=nperio, cd_type='T', psgn=1) 1391 1598 1599 PopStack ( 'zdiv' ) 1392 1600 return zdiv 1393 1601 … … 1399 1607 glam, gphi : longitude and latitude of the points 1400 1608 ''' 1401 1609 PushStack ( 'geo2en (pxx, pyy, pzz, glam, gphi)' ) 1402 1610 gsinlon = np.sin (RAD * glam) 1403 1611 gcoslon = np.cos (RAD * glam) … … 1408 1616 ptn = - pxx * gcoslon * gsinlat - pyy * gsinlon * gsinlat + pzz * gcoslat 1409 1617 1618 PopStack ( geo2en ) 1410 1619 return pte, ptn 1411 1620 … … 1417 1626 glam, gphi : longitude and latitude of the points 1418 1627 ''' 1419 1628 PushStack ( 'en2geo ( pte, ptn, glam, gphi )' ) 1629 1420 1630 gsinlon = np.sin (RAD * glam) 1421 1631 gcoslon = np.cos (RAD * glam) … … 1427 1637 pzz = ptn * gcoslat 1428 1638 1639 PopStack ( 'en2geo' ) 1429 1640 return pxx, pyy, pzz 1430 1641 … … 1434 1645 if needed 1435 1646 ''' 1647 PushStack ( f'clo_lon (lon, {lon0=}, {rad=}, {deg=} )' ) 1648 if rad and deg : 1649 raise 1436 1650 mmath = __mmath__ (lon, np) 1437 if rad : 1438 lon_range = 2.*np.pi 1439 if deg : 1440 lon_range = 360. 1651 if rad : lon_range = 2.*np.pi 1652 if deg : lon_range = 360. 1441 1653 c_lon = lon 1442 c_lon = mmath.where (c_lon > lon0 + lon_range*0.5, 1443 c_lon-lon_range, clo_lon) 1444 c_lon = mmath.where (c_lon < lon0 - lon_range*0.5, 1445 c_lon+lon_range, clo_lon) 1446 c_lon = mmath.where (c_lon > lon0 + lon_range*0.5, 1447 c_lon-lon_range, clo_lon) 1448 c_lon = mmath.where (c_lon < lon0 - lon_range*0.5, 1449 c_lon+lon_range, clo_lon) 1450 if c_lon.shape == () : 1451 c_lon = c_lon.item () 1654 c_lon = mmath.where (c_lon > lon0 + lon_range*0.5, c_lon-lon_range, c_lon) 1655 c_lon = mmath.where (c_lon < lon0 - lon_range*0.5, c_lon+lon_range, c_lon) 1656 c_lon = mmath.where (c_lon > lon0 + lon_range*0.5, c_lon-lon_range, c_lon) 1657 c_lon = mmath.where (c_lon < lon0 - lon_range*0.5, c_lon+lon_range, c_lon) 1658 if c_lon.shape == () : c_lon = c_lon.item () 1452 1659 if mmath == xr : 1453 if lon.attrs : 1454 c_lon.attrs.update ( lon.attrs ) 1660 if lon.attrs : c_lon.attrs.update (lon.attrs) 1661 1662 PopStack ( 'clo_lon' ) 1455 1663 return c_lon 1456 1664 … … 1460 1668 Needed to use transforms in Matplotlib 1461 1669 ''' 1670 # No stack here, to avoid problem in Matplotlib 1671 1462 1672 jpk = gdept_0.shape[0] 1463 1673 kk = xr.DataArray(pk) … … 1474 1684 Needed to use transforms in Matplotlib 1475 1685 ''' 1686 # No stack here, to avoid problem in Matplotlib 1687 1476 1688 jpk = gdept_0.shape[0] 1477 1689 if isinstance (pz, xr.core.dataarray.DataArray ) : 1478 zz = xr.DataArray (pz.values , dims=('zz',))1690 zz = xr.DataArray (pz.values , dims=('zz',)) 1479 1691 elif isinstance (pz, np.ndarray) : 1480 1692 zz = xr.DataArray (pz.ravel(), dims=('zz',)) … … 1497 1709 Needed to use transforms in Matplotlib 1498 1710 ''' 1711 # No stack here, to avoid problem in Matplotlib 1712 1499 1713 jpk = gdept_0.shape[0] 1500 1714 kk = xr.DataArray (pk) … … 1512 1726 Needed to use transforms in Matplotlib 1513 1727 ''' 1728 # No stack here, to avoid problem in Matplotlib 1729 1514 1730 jpk = gdept_0.shape[0] 1515 1731 if isinstance (pz, xr.core.dataarray.DataArray) : … … 1540 1756 Needed to use transforms in Matplotlib 1541 1757 ''' 1758 # No stack here, to avoid problem in Matplotlib 1759 1542 1760 #print ('start depth2comp') 1543 1761 if isinstance (pz, xr.core.dataarray.DataArray) : … … 1549 1767 gz = np.where ( zz>depth0, (zz-depth0)*fact+depth0, zz) 1550 1768 #print ( f'depth2comp : {gz=}' ) 1551 if type (pz) in [int, float] : 1552 return gz.item() 1553 else : 1554 return gz 1769 if type (pz) in [int, float] : return gz.item() 1770 else : return gz 1555 1771 1556 1772 def comp2depth (pz, depth0, fact ) : … … 1559 1775 Needed to use transforms in Matplotlib 1560 1776 ''' 1777 # No stack here, to avoid problem in Matplotlib 1778 1561 1779 if isinstance (pz, xr.core.dataarray.DataArray) : 1562 1780 zz = pz.values … … 1576 1794 Needed to use transforms in Matplotlib 1577 1795 ''' 1796 # No stack here, to avoid problem in Matplotlib 1797 1578 1798 jpi = plon_1d.shape[0] 1579 ii = xr.DataArray (pi)1580 i = np.maximum (0, np.minimum (jpi-1, ii ))1581 i0 = np.floor (i).astype (int)1582 i1 = np.maximum (0, np.minimum (jpi-1, i0+1))1583 xx = i - i01584 gx = (1.0-xx)*plon_1d[i0]+ xx*plon_1d[i1]1799 ii = xr.DataArray (pi) 1800 i = np.maximum (0, np.minimum (jpi-1, ii )) 1801 i0 = np.floor (i).astype (int) 1802 i1 = np.maximum (0, np.minimum (jpi-1, i0+1)) 1803 xx = i - i0 1804 gx = (1.0-xx)*plon_1d[i0]+ xx*plon_1d[i1] 1585 1805 return gx.values 1586 1806 … … 1590 1810 Needed to use transforms in Matplotlib 1591 1811 ''' 1812 # No stack here, to avoid problem in Matplotlib 1813 1592 1814 jpi = plon_1d.shape[0] 1593 1815 if isinstance (px, xr.core.dataarray.DataArray) : … … 1614 1836 Needed to use transforms in Matplotlib 1615 1837 ''' 1838 # No stack here, to avoid problem in Matplotlib 1839 1616 1840 jpj = plat_1d.shape[0] 1617 1841 jj = xr.DataArray (pj) … … 1628 1852 Needed to use transforms in Matplotlib 1629 1853 ''' 1854 # No stack here, to avoid problem in Matplotlib 1855 1630 1856 jpj = plat_1d.shape[0] 1631 1857 if isinstance (py, xr.core.dataarray.DataArray) : … … 1650 1876 respect to east 1651 1877 ''' 1878 PushStack ( f'angle_full ( glamt, gphit, glamu, gphiu, glamv, gphiv, glamf, gphif, {nperio=} )') 1652 1879 mmath = __mmath__ (glamt) 1653 1880 … … 1759 1986 gcosf = gcosf.assign_coords ( glamf.coords ) 1760 1987 1988 PopStack ( 'angle_full' ) 1761 1989 return gsint, gcost, gsinu, gcosu, gsinv, gcosv, gsinf, gcosf 1762 1990 … … 1765 1993 respect to east 1766 1994 ''' 1995 PushStack ( f'angle (glam, gphi, {nperio=}, {cd_type=} ) ') 1767 1996 mmath = __mmath__ (glam) 1768 1997 … … 1797 2026 gcos = gcos.assign_coords ( glam.coords ) 1798 2027 2028 PopStack ( 'angle' ) 1799 2029 return gsin, gcos 1800 2030 1801 def rot_en2ij ( u_e, v_n, gsin, gcos, nperio, cd_type ) :2031 def rot_en2ij ( u_e, v_n, gsin, gcos, nperio, cd_type='T' ) : 1802 2032 '''Rotates the Repere: Change vector componantes between 1803 2033 geographic grid --> stretched coordinates grid. … … 1805 2035 All components are on the same grid (T, U, V or F) 1806 2036 ''' 1807 2037 PushStack ( f'rot_en2ij ( u_e, v_n, gsin, gcos, nperio, {cd_type=} )' ) 1808 2038 u_i = + u_e * gcos + v_n * gsin 1809 2039 v_j = - u_e * gsin + v_n * gcos … … 1812 2042 v_j = lbc (v_j, nperio=nperio, cd_type=cd_type, psgn=-1.0) 1813 2043 2044 PopStack ( 'rot_en2ij' ) 1814 2045 return u_i, v_j 1815 2046 … … 1820 2051 All components are on the same grid (T, U, V or F) 1821 2052 ''' 2053 PushStack ( f'rot_ij2en ( u_i, v_j, gsin, gcos, {nperio=}, {cd_type=} )' ) 1822 2054 u_e = + u_i * gcos - v_j * gsin 1823 2055 v_n = + u_i * gsin + v_j * gcos … … 1826 2058 v_n = lbc (v_n, nperio=nperio, cd_type=cd_type, psgn=1.0) 1827 2059 2060 PopStack ( 'rot_ij2en' ) 1828 2061 return u_e, v_n 1829 2062 … … 1837 2070 Returns east-north components on the T grid point 1838 2071 ''' 2072 PushStack ( f'rot_uv2en ( uo, vo, gsint, gcost, {nperio=}, {zdim=} )' ) 1839 2073 ut = u2t (uo, nperio=nperio, psgn=-1.0, zdim=zdim) 1840 2074 vt = v2t (vo, nperio=nperio, psgn=-1.0, zdim=zdim) … … 1846 2080 v_n = lbc (v_n, nperio=nperio, cd_type='T', psgn=1.0) 1847 2081 2082 PopStack ( 'rot_uv2en' ) 1848 2083 return u_e, v_n 1849 2084 … … 1857 2092 Returns east-north components on the F grid point 1858 2093 ''' 2094 PushStack ( f'rot_uv2enf ( uo, vo, gsint, gcost, {nperio=}, {zdim=} )' ) 1859 2095 uf = u2f (uo, nperio=nperio, psgn=-1.0, zdim=zdim) 1860 2096 vf = v2f (vo, nperio=nperio, psgn=-1.0, zdim=zdim) … … 1866 2102 v_n = lbc (v_n, nperio=nperio, cd_type='F', psgn= 1.0) 1867 2103 2104 PopStack ( 'rot_uv2enf' ) 1868 2105 return u_e, v_n 1869 2106 … … 1871 2108 '''Interpolates an array from U grid to T grid (i-mean) 1872 2109 ''' 2110 PushStack ( f'u2t (utab, {nperio=}, {psgn=}, {zdim=}, {action=} )' ) 1873 2111 mmath = __mmath__ (utab) 1874 2112 utab_0 = mmath.where ( np.isnan(utab), 0., utab) … … 1897 2135 if az != zdim : 1898 2136 ttab = ttab.rename( {az:zdim}) 2137 2138 PopStack ( 'u2t' ) 1899 2139 return ttab 1900 2140 … … 1902 2142 '''Interpolates an array from V grid to T grid (j-mean) 1903 2143 ''' 2144 PushStack ( f'v2t (vtab, {nperio=}, {psgn=}, {zdim=}, {action=} )' ) 1904 2145 mmath = __mmath__ (vtab) 1905 2146 #lperio, aperio = lbc_diag (nperio) … … 1927 2168 if az != zdim : 1928 2169 ttab = ttab.rename( {az:zdim}) 2170 2171 PopStack ( 'v2t' ) 1929 2172 return ttab 1930 2173 … … 1932 2175 '''Interpolates an array from F grid to T grid (i- and j- means) 1933 2176 ''' 2177 PushStack ( f'f2t (ftab, {nperio=}, {psgn=}, {zdim=}, {action=} )' ) 1934 2178 mmath = __mmath__ (ftab) 1935 2179 ftab_0 = mmath.where ( np.isnan(ftab), 0., ftab) … … 1937 2181 ttab = v2t (f2v (ftab_0, nperio=nperio, psgn=psgn, zdim=zdim, action=action), 1938 2182 nperio=nperio, psgn=psgn, zdim=zdim, action=action) 1939 return lbc_del (ttab, nperio=nperio, cd_type='T', psgn=psgn) 2183 2184 ttab = lbc_del (ttab, nperio=nperio, cd_type='T', psgn=psgn) 2185 2186 PopStack ( 'f2t' ) 2187 return ttab 1940 2188 1941 2189 def t2u (ttab, nperio=None, psgn=1.0, zdim=None, action='ave') : 1942 2190 '''Interpolates an array from T grid to U grid (i-mean) 1943 2191 ''' 2192 PushStack ( f't2u (ttab, {nperio=}, {psgn=}, {zdim=}, {action=} )' ) 2193 1944 2194 mmath = __mmath__ (ttab) 1945 2195 ttab_0 = mmath.where ( np.isnan(ttab), 0., ttab) … … 1966 2216 if az != zdim : 1967 2217 utab = utab.rename( {az:zdim}) 2218 2219 PopStack ( 't2u' ) 1968 2220 return utab 1969 2221 … … 1971 2223 '''Interpolates an array from T grid to V grid (j-mean) 1972 2224 ''' 2225 PushStack ( f't2v (ttab, {nperio=}, {psgn=}, {zdim=}, {action=} )' ) 1973 2226 mmath = __mmath__ (ttab) 1974 2227 ttab_0 = mmath.where ( np.isnan(ttab), 0., ttab) … … 1995 2248 if az != zdim : 1996 2249 vtab = vtab.rename( {az:zdim}) 2250 2251 PopStack ( 't2v' ) 1997 2252 return vtab 1998 2253 … … 2000 2255 '''Interpolates an array from V grid to F grid (i-mean) 2001 2256 ''' 2257 PushStack ( f'v2f (vtab, {nperio=}, {psgn=}, {zdim=}, {action=} )' ) 2002 2258 mmath = __mmath__ (vtab) 2003 2259 vtab_0 = mmath.where ( np.isnan(vtab), 0., vtab) … … 2024 2280 if az != zdim : 2025 2281 ftab = ftab.rename( {az:zdim}) 2026 return lbc_del (ftab, nperio=nperio, cd_type='F', psgn=psgn) 2282 2283 ftab = lbc_del (ftab, nperio=nperio, cd_type='F', psgn=psgn) 2284 2285 PopStack ( 'v2f' ) 2286 return ftab 2027 2287 2028 2288 def u2f (utab, nperio=None, psgn=-1.0, zdim=None, action='ave') : 2029 2289 '''Interpolates an array from U grid to F grid i-mean) 2030 2290 ''' 2291 PushStack ( f'u2f (utab, {nperio=}, {psgn=}, {zdim=}, {action=} )' ) 2031 2292 mmath = __mmath__ (utab) 2032 2293 utab_0 = mmath.where ( np.isnan(utab), 0., utab) … … 2053 2314 if az != zdim : 2054 2315 ftab = ftab.rename( {az:zdim}) 2316 2317 PopStack ( 'u2f' ) 2055 2318 return ftab 2056 2319 … … 2058 2321 '''Interpolates an array on T grid to F grid (i- and j- means) 2059 2322 ''' 2323 PushStack ( f't2f (utab, {nperio=}, {psgn=}, {zdim=}, {action=} )' ) 2060 2324 mmath = __mmath__ (ttab) 2061 2325 ttab_0 = mmath.where ( np.isnan(ttab), 0., ttab) … … 2064 2328 nperio=nperio, psgn=psgn, zdim=zdim, action=action) 2065 2329 2066 return lbc_del (ftab, nperio=nperio, cd_type='F', psgn=psgn) 2330 ftab = lbc_del (ftab, nperio=nperio, cd_type='F', psgn=psgn) 2331 2332 PopStack ( 'v2f' ) 2333 return ftab 2067 2334 2068 2335 def f2u (ftab, nperio=None, psgn=1.0, zdim=None, action='ave') : 2069 2336 '''Interpolates an array on F grid to U grid (j-mean) 2070 2337 ''' 2338 PushStack ( f'f2u (utab, {nperio=}, {psgn=}, {zdim=}, {action=} )' ) 2071 2339 mmath = __mmath__ (ftab) 2072 2340 ftab_0 = mmath.where ( np.isnan(ftab), 0., ftab) … … 2091 2359 if zdim and az and az != zdim : 2092 2360 utab = utab.rename( {az:zdim}) 2361 2362 PopStack ( 'f2u' ) 2093 2363 return utab 2094 2364 … … 2096 2366 '''Interpolates an array from F grid to V grid (i-mean) 2097 2367 ''' 2368 PushStack ( f'f2v (ftab, {nperio=}, {psgn=}, {zdim=}, {action=} )' ) 2098 2369 mmath = __mmath__ (ftab) 2099 2370 ftab_0 = mmath.where ( np.isnan(ftab), 0., ftab) … … 2119 2390 if az != zdim : 2120 2391 vtab = vtab.rename( {az:zdim}) 2392 2393 PopStack ( 'f2v' ) 2121 2394 return vtab 2122 2395 … … 2126 2399 sval is the bottom value 2127 2400 ''' 2401 PushStack ( f'w2t (wtab, {zcoord=}, {zdim=}, {sval=} )' ) 2128 2402 mmath = __mmath__ (wtab) 2129 2403 wtab_0 = mmath.where ( np.isnan(wtab), 0., wtab) … … 2146 2420 ttab[..., -1, :, :] = sval 2147 2421 2422 PopStack ( 'w2t' ) 2148 2423 return ttab 2149 2424 … … 2154 2429 if extrap_surf==True, surface value is taken from 1st level value. 2155 2430 ''' 2431 PushStack ( f't2w (utab, {zcoord=}, {zdim=}, {sval=}, {extrap_surf=} )' ) 2156 2432 mmath = __mmath__ (ttab) 2157 2433 ttab_0 = mmath.where ( np.isnan(ttab), 0., ttab) … … 2177 2453 else : 2178 2454 wtab = wtab.assign_coords ( {zdim:np.arange(ttab.shape[kz])+1.} ) 2455 2456 PopStack ( 't2w' ) 2179 2457 return wtab 2180 2458 … … 2186 2464 nperio, cd_type : periodicity characteristics 2187 2465 ''' 2188 2466 PushStack ( f'fill (ptab, {perio=}, {cd_type=}, {npass=}, {sval=} ) ') 2189 2467 mmath = __mmath__ (ptab) 2190 2468 … … 2237 2515 ztab = lbc_del (ztab, nperio=lperio) 2238 2516 2517 PopStack ( 'fill' ) 2239 2518 return ztab 2240 2519 … … 2256 2535 modified eastward/nothward components to have correct polar projections in cartopy 2257 2536 ''' 2537 PushStack (f' correct_uv (u, v, lat)') 2258 2538 uv = np.sqrt (u*u + v*v) # Original modulus 2259 2539 zu = u … … 2262 2542 uc = zu*uv/zz 2263 2543 vc = zv*uv/zz # Final corrected values 2544 2545 PopStack ( 'correct_uv' ) 2264 2546 return uc, vc 2265 2547 … … 2267 2549 '''Returns norm of a 2 components vector 2268 2550 ''' 2269 return np.sqrt (u*u + v*v) 2551 PushStack ( 'norm_uv (u, v)' ) 2552 zz = np.sqrt (u*u + v*v) 2553 Poptack ( 'norm_uv' ) 2554 return zz 2270 2555 2271 2556 def normalize_uv (u, v) : 2272 2557 '''Normalizes 2 components vector 2273 2558 ''' 2559 PushStack ( 'normalize_uv (u, v)' ) 2274 2560 uv = norm_uv (u, v) 2275 return u/uv, v/uv 2561 uu = u/uv 2562 vv = v/uv 2563 PopStack ( 'normalize_uv' ) 2564 return uu, vv 2276 2565 2277 2566 def msf (vv, e1v_e3v, plat1d, depthw) : … … 2281 2570 e1v_e3v : prodcut of scale factors e1v*e3v 2282 2571 ''' 2283 2572 PushStack ( 'msf (vv, e1v_e3v, plat1d, depthw)' ) 2284 2573 v_e1v_e3v = vv * e1v_e3v 2285 2574 v_e1v_e3v.attrs = vv.attrs … … 2301 2590 if az != new_az : 2302 2591 zomsf = zomsf.rename ( {az:new_az} ) 2303 zomsf.attrs ['standard_name'] = 'Meridional stream function'2304 zomsf.attrs ['long_name'] = 'Meridional stream function'2305 zomsf.attrs ['units'] = 'Sv'2306 zomsf [new_az].attrs = depthw.attrs2592 zomsf.attrs ['standard_name'] = 'Meridional stream function' 2593 zomsf.attrs ['long_name'] = 'Meridional stream function' 2594 zomsf.attrs ['units'] = 'Sv' 2595 zomsf [new_az].attrs = depthw.attrs 2307 2596 zomsf.lat.attrs=plat1d.attrs 2308 2597 2598 PopStack ( 'msf' ) 2309 2599 return zomsf 2310 2600 … … 2318 2608 bsf0={'x':5, 'y':300} for eORCA1 2319 2609 ''' 2610 PushStack ( f'bsf (uu, e2u_e3u, mask, {nperio=}, {bsf0=} )' ) 2611 2320 2612 u_e2u_e3u = uu * e2u_e3u 2321 2613 u_e2u_e3u.attrs = uu.attrs … … 2337 2629 zbsf = lbc (zbsf, nperio=nperio, cd_type='F') 2338 2630 2631 PopStack ( 'bsf' ) 2339 2632 return zbsf 2340 2633 … … 2353 2646 2354 2647 ''' 2648 PushStack ( f'namelist_read (ref, cfg, {out=}, {flat=}, {verbose=} )' ) 2355 2649 if ref : 2356 2650 if isinstance (ref, str) : … … 2407 2701 print (nam, ':', value, ':', nml[nam][value]) 2408 2702 2703 PopStack ( 'namelist_read' ) 2409 2704 if out == 'dict' : 2410 2705 return dict_namelist … … 2418 2713 Read NEMO namelist(s) and return either a dictionnary or an xarray dataset 2419 2714 ''' 2715 PushStack ( f'namelist_read [void version] (ref, cfg, {out=}, {flat=}, {verbose=} )' ) 2716 2420 2717 print ( 'Error : module f90nml not found' ) 2421 2718 print ( 'Cannot call namelist_read' ) 2422 2719 print ( 'Call parameters where : ') 2423 2720 print ( f'{err=} {ref=} {cfg=} {out=} {flat=} {verbose=}' ) 2721 PopStack ( 'namelist_read [void version]' ) 2424 2722 2425 2723 def fill_closed_seas (imask, nperio=None, cd_type='T') : … … 2428 2726 imask : mask, 1 on ocean, 0 on land 2429 2727 ''' 2728 PushStack ( f'fill_closed_seas (imask, {nperio=}, {cd_type=} )' ) 2430 2729 from scipy import ndimage 2431 2730 … … 2433 2732 imask_filled = lbc ( imask_filled, nperio=nperio, cd_type=cd_type) 2434 2733 2734 PopStack ( 'fill_closed_seas' ) 2435 2735 return imask_filled 2436 2736 … … 2438 2738 # Sea water state function parameters from NEMO code 2439 2739 2440 RDELTAS = 32. 2740 RDELTAS = 32.0 2441 2741 R1_S0 = 0.875/35.16504 2442 R1_T0 = 1. /40.2443 R1_Z0 = 1. e-42742 R1_T0 = 1.0/40. 2743 R1_Z0 = 1.0e-4 2444 2744 2445 2745 EOS000 = 8.0189615746e+02 … … 2496 2796 EOS013 = 3.7969820455e-01 2497 2797 2498 def rhop ( ptemp, psal) :2798 def rhop (ptemp, psal) : 2499 2799 '''Returns potential density referenced to surface 2500 2800 2501 2801 Computation from NEMO code 2502 2802 ''' 2803 PushStack ( 'rhop ( ptemp, psal )' ) 2503 2804 zt = ptemp * R1_T0 # Temperature (°C) 2504 2805 zs = np.sqrt ( np.abs( psal + RDELTAS ) * R1_S0 ) # Square root of salinity (PSS) … … 2513 2814 + (((((EOS600*zs+ EOS500)*zs + EOS400)*zs + EOS300)*zs + EOS200)*zs + EOS100)*zs + EOS000 ) 2514 2815 # 2816 PopStack ( 'rhop' ) 2515 2817 return prhop 2516 2818 … … 2520 2822 Computation from NEMO code 2521 2823 ''' 2824 PushStack ( 'rho ( ptemp, psal) ' ) 2522 2825 zh = pdep * R1_Z0 # Depth (m) 2523 2826 zt = ptemp * R1_T0 # Temperature (°C) … … 2543 2846 + ((((EOS510*zs + EOS410)*zs + EOS310)*zs + EOS210)*zs + EOS110)*zs + EOS010)*zt 2544 2847 + (((((EOS600*zs + EOS500)*zs + EOS400)*zs + EOS300)*zs + 2545 EOS200)*zs + EOS100)*zs + EOS000 )2848 EOS200)*zs + EOS100)*zs + EOS000 ) 2546 2849 # 2547 2850 prho = ( ( zn3 * zh + zn2 ) * zh + zn1 ) * zh + zn0 2548 2851 # 2852 PopStack ( 'rho' ) 2549 2853 return prho 2550 2854
Note: See TracChangeset
for help on using the changeset viewer.