- Timestamp:
- 10/25/23 11:31:03 (6 months ago)
- Location:
- TOOLS/WATER_BUDGET
- Files:
-
- 6 edited
Legend:
- Unmodified
- Added
- Removed
-
TOOLS/WATER_BUDGET/ATM_waterbudget.py
r6651 r6665 23 23 ### 24 24 ## Import system modules 25 import sys, os, shutil#, subprocess, platform 26 import configparser, re 25 import sys 26 import os 27 import configparser 27 28 28 29 ## Import needed scientific modules 29 import numpy as np, xarray as xr 30 31 # Check python version 32 if sys.version_info < (3, 8, 0) : 33 print ( f'Python version : {platform.python_version()}' ) 34 raise Exception ( "Minimum Python version is 3.8" ) 30 import numpy as np 31 import xarray as xr 35 32 36 33 ## Import local modules 37 34 import WaterUtils as wu 38 35 import libIGCM_sys 39 import nemo, lmdz 40 41 from WaterUtils import VarInt, Rho, Ra, Grav, ICE_rho_ice, ICE_rho_sno, OCE_rho_liq, ATM_rho, SRF_rho, RUN_rho, ICE_rho_pnd, YearLength 36 import lmdz 37 38 from WaterUtils import RA, GRAV, ICE_RHO_ICE, ICE_RHO_SNO, \ 39 OCE_RHO_LIQ, ATM_RHO, SRF_RHO, RUN_RHO, ICE_RHO_PND, YEAR_LENGTH 42 40 43 41 ## Creates parser for reading .ini input file … … 48 46 ## Experiment parameters 49 47 ## --------------------- 50 ATM=None ; ATM_HIS='latlon' ; SRF_HIS='latlon' ; RUN_HIS='latlon' ; ORCA=None ; NEMO=None ; OCE_relax=False 51 OCE_icb=False ; Coupled=False ; Routing=None ; TestInterp=None 52 TarRestartPeriod_beg=None ; TarRestartPeriod_end=None ; Comment=None ; Period=None ; Title=None 48 JobName=None ; TagName=None ; ExperimentName=None ; SpaceName=None 49 SRF=True ; RUN=True 50 ATM=None ; ATM_HIS='latlon' ; SRF_HIS='latlon' ; RUN_HIS='latlon' ; ORCA=None 51 NEMO=None ; OCE_relax=False 52 OCE_icb=False ; Coupled=False ; Rsouting=None ; TestInterp=None 53 TarRestartPeriod_beg=None ; TarRestartPeriod_end=None ; Comment=None 54 Period=None ; Title=None 53 55 YearBegin=None ; YearEnd=None ; DateBegin=None ; DateEnd=None 54 56 Freq=None ; libIGCM=None ; User=None; Group=None ; Routing=None 57 PackFrequency = None ; FreqDir=None 58 59 Timer=False ; Debug=False 55 60 ## 56 ARCHIVE=None ; STORAGE=None ; SCRATCHDIR=None ; R_IN=None ; rebuild=None ; TmpDir=None 57 FileDir=None ; FileOut=None 61 ARCHIVE=None ; STORAGE=None ; SCRATCHDIR=None ; R_IN=None 62 TmpDir=None ; FileDir=None ; FileOut=None ; R_OUT=None ; R_FIG=None 63 R_FIGR=None ; R_BUF=None ; R_BUFR=None ; R_SAVE=None 64 R_BUF_KSH=None ; POST_DIR=None ; L_EXP=None 65 58 66 dir_ATM_his=None ; dir_SRF_his=None ; dir_OCE_his=None ; dir_ICE_his=None 59 67 FileCommon=None ; file_ATM_his=None ; file_SRF_his=None ; file_RUN_his=None 60 68 file_OCE_his=None ; file_ICE_his=None ; file_OCE_sca=None 61 tar_restart_beg=None ; tar_restart_end=None ; file_ATM_beg=None ; file_ATM_end=None ; file_DYN_beg=None 69 tar_restart_beg=None ; tar_restart_end=None ; file_ATM_beg=None 70 file_ATM_end=None ; file_DYN_beg=None 62 71 file_DYN_end=None ; file_SRF_beg=None ; file_SRF_end=None 63 72 file_RUN_beg=None ; file_RUN_end=None ; file_RUN_end=None ; file_OCE_beg=None 64 73 file_ICE_beg=None ; file_OCE_beg=None 65 74 file_OCE_end=None ; file_ICE_beg=None ; file_OCE_end=None ; file_ICE_end=None 75 TarRestartDate_beg=None ; TarRestartDate_end=None 76 file_DYN_aire=None 66 77 tar_restart_beg_ATM=None ; tar_restart_beg_DYN=None ; tar_restart_beg_SRF=None 67 78 tar_restart_beg_RUN=None ; tar_restart_beg_OCE=None ; tar_restart_beg_ICE=None 68 79 tar_restart_end_ATM=None ; tar_restart_end_DYN=None ; tar_restart_end_SRF=None 69 80 tar_restart_end_RUN=None ; tar_restart_end_OCE=None ; tar_restart_end_ICE=None 70 ContinueOnError=False ; ErrorCount=0 # ; SortIco = False 81 ContinueOnError=False ; ErrorCount=0 ; SortIco = False 82 83 FileDirOCE=None ; FileDirATM=None ; FileDirICE=None ; FileDirSRF=None ; FileDirRUN=None 71 84 72 85 ## … … 74 87 ## --------------------------------- 75 88 # Default is float (full precision). Degrade the precision by using np.float32 76 # Restart file are always read at the full precision89 # Restart files are always read at the full precision 77 90 readPrec=float 78 91 … … 88 101 if 'full' in IniFile : FullIniFile = IniFile 89 102 else : FullIniFile = 'full_' + IniFile 90 103 91 104 print ("Input file : ", IniFile ) 92 105 config.read (IniFile) … … 109 122 MyReader = configparser.ConfigParser (interpolation=configparser.ExtendedInterpolation() ) 110 123 MyReader.optionxform = str # To keep capitals 111 124 112 125 MyReader.read (ConfigCard) 113 126 … … 120 133 exec ( f'wu.{VarName} = {VarName}' ) 121 134 print ( f' {VarName:21} set to : {locals()[VarName]:}' ) 122 135 123 136 for VarName in ['PackFrequency'] : 124 137 if VarName in MyReader['Post'].keys() : … … 131 144 else : 132 145 raise FileExistsError ( f"File not found : {ConfigCard = }" ) 133 146 134 147 ## Reading config file 135 148 ## ------------------- 136 for Section in ['Config', 'Experiment', 'libIGCM', 'Files', 'Physics' ] : 137 if Section in config.keys () : 138 print ( f'\nReading [{Section}]' ) 139 for VarName in config[Section].keys() : 140 locals()[VarName] = config[Section][VarName] 141 exec ( f'{VarName} = wu.setBool ({VarName})' ) 142 exec ( f'{VarName} = wu.setNum ({VarName})' ) 143 exec ( f'{VarName} = wu.setNone ({VarName})' ) 144 exec ( f'wu.{VarName} = {VarName}' ) 145 print ( f' {VarName:21} set to : {locals()[VarName]}' ) 146 #exec ( f'del {VarName}' ) 149 # Each entry in the .ini file will create a Python variable with the same name 150 for Section in config.keys () : 151 print ( f'\nReading [{Section}]' ) 152 for VarName in config[Section].keys() : 153 locals()[VarName] = config[Section][VarName] 154 exec ( f'{VarName} = wu.setBool ({VarName})' ) 155 exec ( f'{VarName} = wu.setNum ({VarName})' ) 156 exec ( f'{VarName} = wu.setNone ({VarName})' ) 157 exec ( f'wu.{VarName} = {VarName}' ) 158 print ( f' {VarName:21} set to : {locals()[VarName]}' ) 147 159 148 160 print ( f'\nConfig file readed : {IniFile} ' ) … … 150 162 ## 151 163 ## Reading prec 152 if wu.unDefined ( 'readPrec' ):164 if readPrec : 153 165 readPrec = np.float64 154 166 else : … … 156 168 if readPrec in [ "float32", "r4", "single", "<class 'numpy.float32'>" ] : readPrec = np.float32 157 169 if readPrec in [ "float16", "r2", "half" , "<class 'numpy.float16'>" ] : readPrec = np.float16 158 170 159 171 ## Some physical constants 160 172 ## ======================= 161 if wu.unDefined ( 'Ra' ) : Ra = wu.Ra#-- Earth Radius (m)162 if wu.unDefined ( 'Grav' ) : Grav = wu.Grav#-- Gravity (m^2/s163 if wu.unDefined ( 'ICE_rho_ice' ) : ICE_rho_ice = wu.ICE_rho_ice#-- Ice volumic mass (kg/m3) in LIM3164 if wu.unDefined ( 'ICE_rho_sno') : ICE_rho_sno = wu.ICE_rho_sno#-- Snow volumic mass (kg/m3) in LIM3165 if wu.unDefined ( 'OCE_rho_liq' ) : OCE_rho_liq = wu.OCE_rho_liq#-- Ocean water volumic mass (kg/m3) in NEMO166 if wu.unDefined ( 'ATM_rho' ) : ATM_rho = wu.ATM_rho#-- Water volumic mass in atmosphere (kg/m^3)167 if wu.unDefined ( 'SRF_rho' ) : SRF_rho = wu.SRF_rho#-- Water volumic mass in surface reservoir (kg/m^3)168 if wu.unDefined ( 'RUN_rho' ) : RUN_rho = wu.RUN_rho#-- Water volumic mass of rivers (kg/m^3)169 if wu.unDefined ( 'ICE_rho_pnd' ) : ICE_rho_pnd = wu.ICE_rho_pnd#-- Water volumic mass in ice ponds (kg/m^3)170 if wu.unDefined ( 'YearLength' ) : YearLength = wu.YearLength#-- Year length (s)171 173 if not RA : RA = wu.RA #-- Earth Radius (m) 174 if not GRAV : GRAV = wu.GRAV #-- Gravity (m^2/s 175 if not ICE_RHO_ICE : ICE_RHO_ICE = wu.ICE_RHO_ICE #-- Ice volumic mass (kg/m3) in LIM3 176 if not ICE_RHO_SNO : ICE_RHO_SNO = wu.ICE_RHO_SNO #-- Snow volumic mass (kg/m3) in LIM3 177 if not OCE_RHO_LIQ : OCE_RHO_LIQ = wu.OCE_RHO_LIQ #-- Ocean water volumic mass (kg/m3) in NEMO 178 if not ATM_RHO : ATM_RHO = wu.ATM_RHO #-- Water volumic mass in atmosphere (kg/m^3) 179 if not SRF_RHO : SRF_RHO = wu.SRF_RHO #-- Water volumic mass in surface reservoir (kg/m^3) 180 if not RUN_RHO : RUN_RHO = wu.RUN_RHO #-- Water volumic mass of rivers (kg/m^3) 181 if not ICE_RHO_PND : ICE_RHO_PND = wu.ICE_RHO_PND #-- Water volumic mass in ice ponds (kg/m^3) 182 if not YEAR_LENGTH : YEAR_LENGTH = wu.YEAR_LENGTH #-- Year length (s) 183 172 184 ## Set libIGCM and machine dependant values 173 185 ## ---------------------------------------- 174 if not 'Files' in config.keys () : config['Files'] = {} 175 176 config['Physics'] = { 'Ra':str(Ra), 'Grav':str(Grav), 'ICE_rho_ice':str(ICE_rho_ice), 'ICE_rho_sno':str(ICE_rho_sno), 177 'OCE_rho_liq':str(OCE_rho_liq), 'ATM_rho':str(ATM_rho), 'SRF_rho':str(SRF_rho), 'RUN_rho':str(RUN_rho)} 178 179 config['Config'] = { 'ContinueOnError':str(ContinueOnError), 'TestInterp':str(TestInterp), 'readPrec':str(readPrec) } 186 if 'Files' not in config.keys () : config['Files'] = {} 187 if 'Physics' not in config.keys () : config['Physics'] = {} 188 if 'Config' not in config.keys () : config['Physics'] = {} 189 190 191 config['Physics'].update ( { 'RA':str(RA), 'GRAV':str(GRAV), 'ICE_RHO_ICE':str(ICE_RHO_ICE), 'ICE_RHO_SNO':str(ICE_RHO_SNO), 192 'OCE_RHO_LIQ':str(OCE_RHO_LIQ), 'ATM_RHO':str(ATM_RHO), 'SRF_RHO':str(SRF_RHO), 'RUN_RHO':str(RUN_RHO), 193 'YEAR_LENGTH':str(YEAR_LENGTH)} ) 194 195 config['Config'].update ( { 'ContinueOnError':str(ContinueOnError), 'TestInterp':str(TestInterp), 'readPrec':str(readPrec), 196 'Debug':str(Debug), 'Timer':str(Timer) } ) 180 197 181 198 ## -------------------------- 182 ICO = ( 'ICO' in wu.ATM )183 LMDZ = ( 'LMD' in wu.ATM )199 ICO = 'ICO' in ATM 200 LMDZ = 'LMD' in ATM 184 201 185 202 mm = libIGCM_sys.config ( TagName=TagName, SpaceName=SpaceName, ExperimentName=ExperimentName, JobName=JobName, User=User, Group=Group, 186 ARCHIVE= None, SCRATCHDIR=None, STORAGE=None, R_IN=None, R_OUT=None, R_FIG=None, rebuild=None, TmpDir=None,187 R_SAVE= None, R_FIGR=None, R_BUFR=None, R_BUF_KSH=None, REBUILD_DIR=None, POST_DIR=None)203 ARCHIVE=ARCHIVE, SCRATCHDIR=SCRATCHDIR, STORAGE=STORAGE, R_IN=R_IN, R_OUT=R_OUT, R_FIG=R_FIG, TmpDir=TmpDir, 204 R_SAVE=R_SAVE, R_FIGR=R_FIGR, R_BUFR=R_BUFR, R_BUF_KSH=R_BUF_KSH, POST_DIR=POST_DIR, L_EXP=L_EXP ) 188 205 globals().update(mm) 189 206 190 207 config['Files']['TmpDir'] = TmpDir 191 config['libIGCM'] = { 'ARCHIVE':ARCHIVE, 'STORAGE':STORAGE, 'TmpDir':TmpDir, 'R_IN':R_IN, 'rebuild':rebuild } 208 209 if 'libIGCM' not in config.keys () : config['libIGCM'] = {} 210 config['libIGCM'].update ( { 'ARCHIVE':str(ARCHIVE), 'STORAGE':str(STORAGE), 'TmpDir':str(TmpDir), 'R_IN':str(R_IN) } ) 211 212 ## Debuging and timer 213 Timer = wu.functools.partial (wu.Timer, debug=Debug, timer=Timer) 192 214 193 215 ## Defines begining and end of experiment 194 216 ## -------------------------------------- 195 if wu.unDefined ( 'DateBegin' ):217 if not DateBegin : 196 218 DateBegin = f'{YearBegin}0101' 197 219 config['Experiment']['DateBegin'] = str(DateBegin) … … 201 223 config['Experiment']['YearBegin'] = str(YearBegin) 202 224 203 if wu.unDefined ( 'DateEnd' ):225 if not DateEnd : 204 226 DateEnd = f'{YearEnd}1231' 205 227 config['Experiment']['DateEnd'] = str(DateEnd) … … 209 231 config['Experiment']['DateEnd'] = str(DateEnd) 210 232 211 if wu.unDefined ( 'PackFrequency' ):233 if not PackFrequency : 212 234 PackFrequency = YearEnd - YearBegin + 1 213 235 config['Experiment']['PackFrequency'] = f'{PackFrequency}' 214 236 215 if type ( PackFrequency ) == str :216 if 'Y' in PackFrequency : PackFrequency = PackFrequency.replace ( 'Y', '') 237 if isinstance ( PackFrequency, str) : 238 if 'Y' in PackFrequency : PackFrequency = PackFrequency.replace ( 'Y', '') 217 239 if 'M' in PackFrequency : PackFrequency = PackFrequency.replace ( 'M', '') 218 240 PackFrequency = int ( PackFrequency ) 219 241 220 242 ## Output file with water budget diagnostics 221 243 ## ----------------------------------------- 222 if wu.unDefined ( 'FileOut' ):244 if not FileOut : 223 245 FileOut = f'ATM_waterbudget_{JobName}_{YearBegin}_{YearEnd}' 224 if ICO : 246 if ICO : 225 247 if ATM_HIS == 'latlon' : FileOut = f'{FileOut}_LATLON' 226 248 if ATM_HIS == 'ico' : FileOut = f'{FileOut}_ICO' … … 230 252 config['Files']['FileOut'] = FileOut 231 253 232 f_out = open ( FileOut, mode = 'w' )254 f_out = open ( FileOut, mode = 'w', encoding="utf-8" ) 233 255 234 256 ## Useful functions 235 257 ## ---------------- 236 if readPrec == float : 237 def rprec (tab) : return tab 258 if repr(readPrec) == "<class 'numpy.float64'>" or readPrec == float : 259 def rprec (ptab) : 260 '''This version does nothing 261 262 rprec may be used to reduce floating precision when reading history files 263 ''' 264 return ptab 238 265 else : 239 def rprec (tab) : return tab.astype(readPrec).astype(float) 240 241 def kg2Sv (val, rho=ATM_rho) : 266 def rprec (ptab) : 267 '''Returns float with a different precision''' 268 return ptab.astype(readPrec).astype(float) 269 270 def kg2Sv (pval, rho=ATM_RHO) : 242 271 '''From kg to Sverdrup''' 243 return val/dtime_sec*1.0e-6/rho244 245 def kg2myear ( val, rho=ATM_rho) :272 return pval/dtime_sec*1.0e-6/rho 273 274 def kg2myear (pval, rho=ATM_RHO) : 246 275 '''From kg to m/year''' 247 return val/ATM_aire_sea_tot/rho/NbYear 248 249 def var2prt (var, small=False, rho=ATM_rho) : 250 if small : return var , kg2Sv(var, rho=rho)*1000., kg2myear(var, rho=rho)*1000 251 else : return var , kg2Sv(var, rho=rho) , kg2myear(var, rho=rho) 252 253 def prtFlux (Desc, var, Form='F', small=False, rho=ATM_rho, width=15) : 276 return pval/ATM_aire_sea_tot/rho/NbYear 277 278 def var2prt (pvar, small=False, rho=ATM_RHO) : 279 '''Formats value for printing''' 280 if small : return pvar, kg2Sv (pvar, rho=rho)*1000., kg2myear (pvar, rho=rho)*1000 281 else : return pvar, kg2Sv (pvar, rho=rho) , kg2myear (pvar, rho=rho) 282 283 def prtFlux (Desc, pvar, Form='F', small=False, rho=ATM_RHO, width=15) : 284 '''Pretty print of formattd value''' 254 285 if small : 255 286 if Form in ['f', 'F'] : ff=" {:14.6e} kg | {:12.4f} mSv | {:12.4f} mm/year " … … 258 289 if Form in ['f', 'F'] : ff=" {:14.6e} kg | {:12.4f} Sv | {:12.4f} m/year " 259 290 if Form in ['e', 'E'] : ff=" {:14.6e} kg | {:12.4e} Sv | {:12.4e} m/year " 260 echo ( (' {:>{width}} = ' +ff).format (Desc, *var2prt(var, small=small, rho=rho), width=width ) ) 261 return None 291 echo ( (' {:>{width}} = ' +ff).format (Desc, *var2prt (pvar, small=small, rho=rho), width=width ) ) 262 292 263 293 def echo (string, end='\n') : … … 267 297 f_out.write ( str(string) + end ) 268 298 f_out.flush () 269 return None270 299 271 300 echo ( f'{ContinueOnError = }' ) … … 274 303 echo ( f'{JobName = }' ) 275 304 echo ( f'{ConfigCard = }' ) 276 echo ( f'{libIGCM = }' ) 277 echo ( f'{User = }' ) 278 echo ( f'{Group = }' ) 279 echo ( f'{Freq = }' ) 280 echo ( f'{YearBegin = }' ) 281 echo ( f'{YearEnd = }' ) 305 echo ( f'{libIGCM = }' ) 306 echo ( f'{User = }' ) 307 echo ( f'{Group = }' ) 308 echo ( f'{Freq = }' ) 309 echo ( f'{YearBegin = }' ) 310 echo ( f'{YearEnd = }' ) 282 311 echo ( f'{DateBegin = }' ) 283 312 echo ( f'{DateEnd = }' ) 284 echo ( f'{PackFrequency = }' ) 285 echo ( f'{ATM = }' ) 286 echo ( f'{Routing = }' ) 287 echo ( f'{ORCA = }' ) 288 echo ( f'{NEMO = }' ) 289 echo ( f'{Coupled = }' ) 290 echo ( f'{ATM_HIS = }' ) 291 echo ( f'{SRF_HIS = }' ) 292 echo ( f'{RUN_HIS = }' ) 313 echo ( f'{PackFrequency = }' ) 314 echo ( f'{ATM = }' ) 315 echo ( f'{Routing = }' ) 316 echo ( f'{ORCA = }' ) 317 echo ( f'{NEMO = }' ) 318 echo ( f'{Coupled = }' ) 319 echo ( f'{ATM_HIS = }' ) 320 echo ( f'{SRF_HIS = }' ) 321 echo ( f'{RUN_HIS = }' ) 293 322 294 323 ## Set libIGCM directories 295 324 ## ----------------------- 296 if wu.unDefined ('R_OUT' ) : R_OUT = os.path.join ( ARCHIVE , 'IGCM_OUT' ) 297 if wu.unDefined ('R_BUF' ) : R_BUF = os.path.join ( SCRATCHDIR, 'IGCM_OUT' ) 298 if wu.unDefined ('L_EXP' ) : L_EXP = os.path.join (TagName, SpaceName, ExperimentName, JobName) 299 if wu.unDefined ('R_SAVE' ) : R_SAVE = os.path.join ( R_OUT, L_EXP ) 300 if wu.unDefined ('R_BUFR' ) : R_BUFR = os.path.join ( R_BUF, L_EXP ) 301 if wu.unDefined ('POST_DIR' ) : POST_DIR = os.path.join ( R_BUFR, 'Out' ) 302 if wu.unDefined ('REBUILD_DIR') : REBUILD_DIR = os.path.join ( R_BUFR, 'REBUILD' ) 303 if wu.unDefined ('R_BUF_KSH' ) : R_BUF_KSH = os.path.join ( R_BUFR, 'Out' ) 304 if wu.unDefined ('R_FIGR' ) : R_FIGR = os.path.join ( STORAGE, 'IGCM_OUT', L_EXP ) 305 306 config['libIGCM'].update ( { 'R_OUT':R_OUT, 'R_BUF':R_BUF, 'L_EXP':L_EXP, 'R_BUFR':R_BUFR, 'POST_DIR':POST_DIR, 307 'REBUILD_DIR':REBUILD_DIR, 'R_BUF_KSH':R_BUF_KSH, 'R_FIGR':R_FIGR, 'rebuild':rebuild } ) 325 if not R_OUT : R_OUT = os.path.join ( ARCHIVE , 'IGCM_OUT' ) 326 if not R_BUF : R_BUF = os.path.join ( SCRATCHDIR, 'IGCM_OUT' ) 327 if not L_EXP : 328 if TagName and SpaceName and ExperimentName and JobName : L_EXP = os.path.join (TagName, SpaceName, ExperimentName, JobName) 329 else : L_EXP = '/' 330 if not R_SAVE : R_SAVE = os.path.join ( R_OUT, L_EXP ) 331 if not R_BUFR : R_BUFR = os.path.join ( R_BUF, L_EXP ) 332 if not POST_DIR : POST_DIR = os.path.join ( R_BUFR, 'Out' ) 333 if not R_BUF_KSH : R_BUF_KSH = os.path.join ( R_BUFR, 'Out' ) 334 if not R_FIGR : R_FIGR = os.path.join ( STORAGE, 'IGCM_OUT', L_EXP ) 335 336 config['libIGCM'].update ( { 'R_OUT':R_OUT, 'R_BUF':R_BUF, 'L_EXP':L_EXP, 'R_BUFR':R_BUFR, 'POST_DIR':POST_DIR, 'R_SAVE':R_SAVE, 337 'R_BUF_KSH':R_BUF_KSH, 'R_FIGR':R_BUFR } ) 308 338 309 339 ## Set directory to extract files 310 340 ## ------------------------------ 311 if wu.unDefined ( 'FileDir' ): FileDir = os.path.join ( TmpDir, f'WATER_{JobName}' )341 if not FileDir : FileDir = os.path.join ( TmpDir, f'WATER_{JobName}' ) 312 342 config['Files']['FileDir'] = FileDir 313 343 314 344 if not os.path.isdir ( FileDir ) : os.makedirs ( FileDir ) 315 316 ##- Set directories to rebuild ocean and ice restart files317 if wu.unDefined ( 'FileDirOCE' ) : FileDirOCE = os.path.join ( FileDir, 'OCE' )318 if wu.unDefined ( 'FileDirICE' ) : FileDirICE = os.path.join ( FileDir, 'ICE' )319 if not os.path.exists ( FileDirOCE ) : os.mkdir ( FileDirOCE )320 if not os.path.exists ( FileDirICE ) : os.mkdir ( FileDirICE )321 345 322 346 echo (' ') … … 325 349 echo ( f'TmpDir : {TmpDir}' ) 326 350 echo ( f'FileDir : {FileDir}' ) 327 echo ( f'FileDirOCE : {FileDirOCE}' )328 echo ( f'FileDirICE : {FileDirICE}' )329 351 330 352 echo ( f'\nDealing with {L_EXP}' ) … … 334 356 if Freq == "MO" : FreqDir = os.path.join ('Output' , 'MO' ) 335 357 if Freq == "SE" : FreqDir = os.path.join ('Analyse', 'SE' ) 336 if wu.unDefined ('dir_ATM_his' ):358 if not dir_ATM_his : 337 359 dir_ATM_his = os.path.join ( R_SAVE, "ATM", FreqDir ) 338 360 config['Files']['dir_ATM_his'] = dir_ATM_his 339 if wu.unDefined ( 'dir_SRF_his' ) :361 if not dir_SRF_his : 340 362 dir_SRF_his = os.path.join ( R_SAVE, "SRF", FreqDir ) 341 363 config['Files']['dir_SRF_his'] = dir_SRF_his 342 343 echo ( f'The analysis relies on files from the following model output directories : ' )364 365 echo ( 'The analysis relies on files from the following model output directories : ' ) 344 366 echo ( f'{dir_ATM_his = }' ) 345 367 echo ( f'{dir_SRF_his = }' ) 346 368 347 369 ##-- Creates files names 348 if wu.unDefined ( 'Period' ):370 if not Period : 349 371 if Freq == 'MO' : Period = f'{DateBegin}_{DateEnd}_1M' 350 372 if Freq == 'SE' : Period = f'SE_{DateBegin}_{DateEnd}_1M' … … 353 375 config['Files']['DateBegin'] = DateBegin 354 376 config['Files']['DateBegin'] = DateEnd 355 377 356 378 echo ( f'Period : {Period}' ) 357 358 if wu.unDefined ( 'FileCommon' ):379 380 if not FileCommon : 359 381 FileCommon = f'{JobName}_{Period}' 360 382 config['Files']['FileCommon'] = FileCommon 361 383 362 if wu.unDefined ( 'Title' ):384 if not Title : 363 385 Title = f'{JobName} : {Freq} : {DateBegin} - {DateEnd}' 364 386 config['Files']['Title'] = Title 365 387 366 388 echo ('\nOpen history files' ) 367 if wu.unDefined ( 'file_ATM_his' ):389 if not file_ATM_his : 368 390 if ATM_HIS == 'latlon' : 369 391 file_ATM_his = os.path.join ( dir_ATM_his, f'{FileCommon}_histmth.nc' ) … … 371 393 file_ATM_his = os.path.join ( dir_ATM_his, f'{FileCommon}_histmth_ico.nc' ) 372 394 config['Files']['file_ATM_his'] = file_ATM_his 373 if wu.unDefined ( 'file_SRF_his' ):395 if not file_SRF_his : 374 396 if ATM_HIS == 'latlon' : 375 397 file_SRF_his = os.path.join ( dir_SRF_his, f'{FileCommon}_sechiba_history.nc' ) … … 378 400 config['Files']['file_SRF_his'] = file_SRF_his 379 401 380 if Routing == 'SIMPLE' :381 if file_RUN_his ==None :402 if SRF and Routing == 'SIMPLE' : 403 if file_RUN_his is None : 382 404 if ATM_HIS == 'latlon' : 383 405 file_RUN_his = os.path.join ( dir_SRF_his, f'{FileCommon}_sechiba_history.nc' ) … … 387 409 388 410 d_ATM_his = xr.open_dataset ( file_ATM_his, use_cftime=True, decode_times=True, decode_cf=True ).squeeze() 389 d_SRF_his = xr.open_dataset ( file_SRF_his, use_cftime=True, decode_times=True, decode_cf=True ).squeeze() 390 if Routing == 'SECHIBA' : d_RUN_his = d_SRF_his 391 if Routing == 'SIMPLE' : d_RUN_his = xr.open_dataset ( file_RUN_his, use_cftime=True, decode_times=True, decode_cf=True ).squeeze() 411 if SRF : 412 d_SRF_his = xr.open_dataset ( file_SRF_his, use_cftime=True, decode_times=True, decode_cf=True ).squeeze() 413 if Routing == 'SECHIBA' : d_RUN_his = d_SRF_his 414 if Routing == 'SIMPLE' : d_RUN_his = xr.open_dataset ( file_RUN_his, use_cftime=True, decode_times=True, decode_cf=True ).squeeze() 392 415 393 416 echo ( f'{file_ATM_his = }' ) 394 echo ( f'{file_SRF_his = }' ) 395 if Routing == 'SIMPLE' : echo ( f'{file_RUN_his = }' ) 417 if SRF : 418 echo ( f'{file_SRF_his = }' ) 419 if Routing == 'SIMPLE' : echo ( f'{file_RUN_his = }' ) 396 420 397 421 ## Compute run length … … 409 433 410 434 ##-- Number of years (approximative) 411 NbYear = dtime_sec / Y earLength435 NbYear = dtime_sec / YEAR_LENGTH 412 436 413 437 ##-- Extract restart files from tar 414 438 415 if wu.unDefined ('TarRestartDate_beg' ): TarRestartDate_beg = wu.DateMinusOneDay ( DateBegin )416 if wu.unDefined ('TarRestartDate_end' ): TarRestartDate_end = wu.FormatToGregorian ( DateEnd )417 418 if wu.unDefined ( 'TarRestartPeriod_beg' ):439 if not TarRestartDate_beg : TarRestartDate_beg = wu.DateMinusOneDay ( DateBegin ) 440 if not TarRestartDate_end : TarRestartDate_end = wu.FormatToGregorian ( DateEnd ) 441 442 if not TarRestartPeriod_beg : 419 443 420 444 TarRestartPeriod_beg_DateEnd = TarRestartDate_beg 421 445 TarRestartPeriod_beg_DateBeg = wu.DateAddYear ( TarRestartPeriod_beg_DateEnd, -PackFrequency ) 422 446 TarRestartPeriod_beg_DateBeg = wu.DatePlusOneDay ( TarRestartPeriod_beg_DateBeg ) 423 447 424 448 TarRestartPeriod_beg = f'{TarRestartPeriod_beg_DateBeg}_{TarRestartPeriod_beg_DateEnd}' 425 449 echo (f'Tar period for initial restart : {TarRestartPeriod_beg}') 426 450 config['Files']['TarRestartPeriod_beg'] = TarRestartPeriod_beg 427 451 428 if wu.unDefined ( 'TarRestartPeriod_end' ):452 if not TarRestartPeriod_end : 429 453 430 454 TarRestartPeriod_end_DateEnd = TarRestartDate_end 431 455 TarRestartPeriod_end_DateBeg = wu.DateAddYear ( TarRestartPeriod_end_DateEnd, -PackFrequency ) 432 456 TarRestartPeriod_end_DateBeg = wu.DatePlusOneDay ( TarRestartPeriod_end_DateBeg ) 433 457 434 458 TarRestartPeriod_end = f'{TarRestartPeriod_end_DateBeg}_{TarRestartPeriod_end_DateEnd}' 435 459 echo (f'Tar period for final restart : {TarRestartPeriod_end}') … … 438 462 echo (f'Restart dates - Start : {TarRestartPeriod_beg} / End : {TarRestartPeriod_end}') 439 463 440 if wu.unDefined ( 'tar_restart_beg' ):464 if not tar_restart_beg : 441 465 tar_restart_beg = os.path.join ( R_SAVE, 'RESTART', f'{JobName}_{TarRestartPeriod_beg}_restart.tar' ) 442 466 config['Files']['tar_restart_beg'] = tar_restart_beg 443 if wu.unDefined ( 'tar_restart_end' ):467 if not tar_restart_end : 444 468 tar_restart_end = os.path.join ( R_SAVE, 'RESTART', f'{JobName}_{TarRestartPeriod_end}_restart.tar' ) 445 469 config['Files']['tar_restart_end'] = tar_restart_end 446 470 447 471 echo ( f'{tar_restart_beg = }' ) 448 472 echo ( f'{tar_restart_end = }' ) 449 473 450 474 ##-- Names of tar files with restarts 451 if wu.unDefined ( 'SRF_HIS' ) : SRF_HIS = ATM_HIS 452 453 if wu.unDefined ( 'tar_restart_beg_ATM' ) : tar_restart_beg_ATM = tar_restart_beg 454 if wu.unDefined ( 'tar_restart_beg_DYN' ) : tar_restart_beg_DYN = tar_restart_beg 455 if wu.unDefined ( 'tar_restart_beg_SRF' ) : tar_restart_beg_SRF = tar_restart_beg 456 if wu.unDefined ( 'tar_restart_beg_RUN' ) : tar_restart_beg_RUN = tar_restart_beg 457 if wu.unDefined ( 'tar_restart_beg_OCE' ) : tar_restart_beg_OCE = tar_restart_beg 458 if wu.unDefined ( 'tar_restart_beg_ICE' ) : tar_restart_beg_ICE = tar_restart_beg 459 460 if wu.unDefined ( 'tar_restart_end_ATM' ) : tar_restart_end_ATM = tar_restart_end 461 if wu.unDefined ( 'tar_restart_end_DYN' ) : tar_restart_end_DYN = tar_restart_end 462 if wu.unDefined ( 'tar_restart_end_SRF' ) : tar_restart_end_SRF = tar_restart_end 463 if wu.unDefined ( 'tar_restart_end_RUN' ) : tar_restart_end_RUN = tar_restart_end 464 if wu.unDefined ( 'tar_restart_end_OCE' ) : tar_restart_end_OCE = tar_restart_end 465 if wu.unDefined ( 'tar_restart_end_ICE' ) : tar_restart_end_ICE = tar_restart_end 466 467 if wu.unDefined ( 'file_ATM_beg' ) : 475 476 if not tar_restart_beg_ATM : tar_restart_beg_ATM = tar_restart_beg 477 if not tar_restart_beg_DYN : tar_restart_beg_DYN = tar_restart_beg 478 if not tar_restart_beg_RUN : tar_restart_beg_RUN = tar_restart_beg 479 if not tar_restart_beg_OCE : tar_restart_beg_OCE = tar_restart_beg 480 if not tar_restart_beg_ICE : tar_restart_beg_ICE = tar_restart_beg 481 482 if not tar_restart_end_ATM : tar_restart_end_ATM = tar_restart_end 483 if not tar_restart_end_DYN : tar_restart_end_DYN = tar_restart_end 484 if not tar_restart_end_RUN : tar_restart_end_RUN = tar_restart_end 485 if not tar_restart_end_OCE : tar_restart_end_OCE = tar_restart_end 486 if not tar_restart_end_ICE : tar_restart_end_ICE = tar_restart_end 487 488 if SRF : 489 if not SRF_HIS : SRF_HIS = ATM_HIS 490 if not tar_restart_beg_SRF : tar_restart_beg_SRF = tar_restart_beg 491 if not tar_restart_end_SRF : tar_restart_end_SRF = tar_restart_end 492 493 if not file_ATM_beg : 468 494 file_ATM_beg = f'{FileDir}/ATM_{JobName}_{TarRestartDate_beg}_restartphy.nc' 469 495 config['Files']['file_ATM_beg'] = file_ATM_beg 470 if wu.unDefined ( 'file_ATM_end' ):496 if not file_ATM_end : 471 497 file_ATM_end = f'{FileDir}/ATM_{JobName}_{TarRestartDate_end}_restartphy.nc' 472 498 config['Files']['file_ATM_end'] = file_ATM_end … … 475 501 liste_end = [file_ATM_end, ] 476 502 477 if wu.unDefined ( 'file_DYN_beg' ):503 if not file_DYN_beg : 478 504 if LMDZ : file_DYN_beg = f'{FileDir}/ATM_{JobName}_{TarRestartDate_beg}_restart.nc' 479 505 if ICO : file_DYN_beg = f'{FileDir}/ICO_{JobName}_{TarRestartDate_beg}_restart.nc' 480 506 liste_beg.append (file_DYN_beg) 481 507 config['Files']['file_DYN_beg'] = file_DYN_beg 482 483 if wu.unDefined ( 'file_DYN_end' ) :508 509 if not file_DYN_end : 484 510 if LMDZ : file_DYN_end = f'{FileDir}/ATM_{JobName}_{TarRestartDate_end}_restart.nc' 485 511 if ICO : file_DYN_end = f'{FileDir}/ICO_{JobName}_{TarRestartDate_end}_restart.nc' … … 487 513 config['Files']['file_DYN_end'] = file_DYN_end 488 514 489 if wu.unDefined ( 'file_SRF_beg' ) : 490 file_SRF_beg = f'{FileDir}/SRF_{JobName}_{TarRestartDate_beg}_sechiba_rest.nc' 491 config['Files']['file_SRF_beg'] = file_SRF_beg 492 if wu.unDefined ( 'file_SRF_end' ) : 493 file_SRF_end = f'{FileDir}/SRF_{JobName}_{TarRestartDate_end}_sechiba_rest.nc' 494 config['Files']['file_SRF_end'] = file_SRF_end 495 515 if SRF : 516 if not file_SRF_beg : 517 file_SRF_beg = f'{FileDir}/SRF_{JobName}_{TarRestartDate_beg}_sechiba_rest.nc' 518 config['Files']['file_SRF_beg'] = file_SRF_beg 519 if not file_SRF_end : 520 file_SRF_end = f'{FileDir}/SRF_{JobName}_{TarRestartDate_end}_sechiba_rest.nc' 521 config['Files']['file_SRF_end'] = file_SRF_end 522 496 523 liste_beg.append ( file_SRF_beg ) 497 524 liste_end.append ( file_SRF_end ) … … 501 528 echo ( f'{file_DYN_beg = }') 502 529 echo ( f'{file_DYN_end = }') 503 echo ( f'{file_SRF_beg = }') 504 echo ( f'{file_SRF_end = }') 530 if SRF : 531 echo ( f'{file_SRF_beg = }') 532 echo ( f'{file_SRF_end = }') 505 533 506 534 if ICO : 507 if wu.unDefined ('file_DYN_aire'): file_DYN_aire = os.path.join ( R_IN, 'ATM', 'GRID', ATM+'_grid.nc' )535 if not file_DYN_aire : file_DYN_aire = os.path.join ( R_IN, 'ATM', 'GRID', ATM+'_grid.nc' ) 508 536 config['Files']['file_DYN_aire'] = file_DYN_aire 509 537 510 if Routing == 'SIMPLE' :511 if wu.unDefined ( 'file_RUN_beg' ) :538 if SRF and Routing == 'SIMPLE' : 539 if not file_RUN_beg : 512 540 file_RUN_beg = f'{FileDir}/SRF_{JobName}_{TarRestartDate_beg}_routing_restart.nc' 513 541 config['Files']['file_RUN_beg'] = file_RUN_beg 514 if wu.unDefined ( 'file_RUN_end' ) :542 if not file_RUN_end : 515 543 file_RUN_end = f'{FileDir}/SRF_{JobName}_{TarRestartDate_end}_routing_restart.nc' 516 544 config['Files']['file_RUN_end'] = file_RUN_end 517 545 518 546 liste_beg.append ( file_RUN_beg ) 519 547 liste_end.append ( file_RUN_end ) … … 521 549 echo ( f'{file_RUN_end = }' ) 522 550 523 echo ('\nExtract restart files from tar : ATM, ICO and SRF') 524 525 def extract_and_rebuild ( file_name=file_ATM_beg, tar_restart=tar_restart_end, FileDirComp=FileDir, ErrorCount=ErrorCount ) : 551 echo ('\nExtract restart files from tar : ATM, ICO', end='') 552 if SRF : echo ( ' and SRF') 553 else : echo (' ') 554 555 556 @Timer 557 def extract ( file_name=file_ATM_beg, tar_restart=tar_restart_end, file_dir_comp=FileDir, error_count=ErrorCount ) : 558 ''' 559 Extract restart files from a tar 560 ''' 526 561 echo ( f'----------') 527 562 echo ( f'file to extract : {file_name = }' ) … … 532 567 base_resFile = os.path.basename (file_name) 533 568 if os.path.exists ( tar_restart ) : 534 command = f'cd { FileDir} ; tar xf {tar_restart} {base_resFile}'569 command = f'cd {file_dir_comp} ; tar xf {tar_restart} {base_resFile}' 535 570 echo ( f'{command = }' ) 536 571 try : os.system ( command ) 537 572 except : 538 573 if ContinueOnError : 539 ErrorCount += 1540 echo ( f'****** Command failed : {command}' )541 echo ( f'****** Trying to continue' )574 error_count += 1 575 echo ( '****** Command failed : {command}' ) 576 echo ( '****** Trying to continue' ) 542 577 echo ( ' ') 543 578 else : 544 raise Exception( f'**** command failed : {command} - Stopping' )579 raise OSError ( f'**** command failed : {command} - Stopping' ) 545 580 else : 546 581 echo ( f'tar done : {base_resFile}' ) … … 548 583 echo ( f'****** Tar restart file {tar_restart = } not found ' ) 549 584 if ContinueOnError : 550 ErrorCount += 1 551 else : 552 raise Exception ( f'****** tar file not found {tar_restart = } - Stopping' ) 553 return ErrorCount 554 555 ErrorCount += extract_and_rebuild ( file_name=file_ATM_beg, tar_restart=tar_restart_beg_ATM, FileDirComp=FileDir ) 556 ErrorCount += extract_and_rebuild ( file_name=file_DYN_beg, tar_restart=tar_restart_beg_DYN, FileDirComp=FileDir ) 557 ErrorCount += extract_and_rebuild ( file_name=file_SRF_beg, tar_restart=tar_restart_beg_SRF, FileDirComp=FileDir ) 558 559 ErrorCount += extract_and_rebuild ( file_name=file_ATM_end, tar_restart=tar_restart_end_ATM, FileDirComp=FileDir ) 560 ErrorCount += extract_and_rebuild ( file_name=file_DYN_end, tar_restart=tar_restart_end_DYN, FileDirComp=FileDir ) 561 ErrorCount += extract_and_rebuild ( file_name=file_SRF_end, tar_restart=tar_restart_end_SRF, FileDirComp=FileDir ) 562 563 if Routing == 'SIMPLE' : 564 ErrorCount += extract_and_rebuild ( file_name=file_RUN_beg, tar_restart=tar_restart_beg_RUN, FileDirComp=FileDir ) 565 ErrorCount += extract_and_rebuild ( file_name=file_RUN_end, tar_restart=tar_restart_end_RUN, FileDirComp=FileDir ) 585 error_count += 1 586 else : 587 raise OSError ( f'****** tar file not found {tar_restart = } - Stopping' ) 588 return error_count 589 590 ErrorCount += extract ( file_name=file_ATM_beg, tar_restart=tar_restart_beg_ATM, file_dir_comp=FileDir, error_count=ErrorCount ) 591 ErrorCount += extract ( file_name=file_DYN_beg, tar_restart=tar_restart_beg_DYN, file_dir_comp=FileDir, error_count=ErrorCount ) 592 593 ErrorCount += extract ( file_name=file_ATM_end, tar_restart=tar_restart_end_ATM, file_dir_comp=FileDir, error_count=ErrorCount ) 594 ErrorCount += extract ( file_name=file_DYN_end, tar_restart=tar_restart_end_DYN, file_dir_comp=FileDir, error_count=ErrorCount ) 595 596 if SRF : 597 ErrorCount += extract ( file_name=file_SRF_beg, tar_restart=tar_restart_beg_SRF, file_dir_comp=FileDir, error_count=ErrorCount ) 598 ErrorCount += extract ( file_name=file_SRF_end, tar_restart=tar_restart_end_SRF, file_dir_comp=FileDir, error_count=ErrorCount ) 599 600 if Routing == 'SIMPLE' : 601 ErrorCount += extract ( file_name=file_RUN_beg, tar_restart=tar_restart_beg_RUN, file_dir_comp=FileDir, error_count=ErrorCount ) 602 ErrorCount += extract ( file_name=file_RUN_end, tar_restart=tar_restart_end_RUN, file_dir_comp=FileDir, error_count=ErrorCount ) 566 603 567 604 ##-- Exit in case of error in the opening file phase 568 605 if ErrorCount > 0 : 569 606 echo ( ' ' ) 570 raise Exception( f'**** Some files missing - Stopping - {ErrorCount = }' )571 572 ## 607 raise RuntimeError ( f'**** Some files missing - Stopping - {ErrorCount = }' ) 608 609 ## 573 610 echo ('\nOpening ATM SRF and ICO restart files') 574 611 d_ATM_beg = xr.open_dataset ( os.path.join (FileDir, file_ATM_beg), decode_times=False, decode_cf=True ).squeeze() 575 612 d_ATM_end = xr.open_dataset ( os.path.join (FileDir, file_ATM_end), decode_times=False, decode_cf=True ).squeeze() 576 d_SRF_beg = xr.open_dataset ( os.path.join (FileDir, file_SRF_beg), decode_times=False, decode_cf=True ).squeeze() 577 d_SRF_end = xr.open_dataset ( os.path.join (FileDir, file_SRF_end), decode_times=False, decode_cf=True ).squeeze() 613 if SRF : 614 d_SRF_beg = xr.open_dataset ( os.path.join (FileDir, file_SRF_beg), decode_times=False, decode_cf=True ).squeeze() 615 d_SRF_end = xr.open_dataset ( os.path.join (FileDir, file_SRF_end), decode_times=False, decode_cf=True ).squeeze() 578 616 d_DYN_beg = xr.open_dataset ( os.path.join (FileDir, file_DYN_beg), decode_times=False, decode_cf=True ).squeeze() 579 617 d_DYN_end = xr.open_dataset ( os.path.join (FileDir, file_DYN_end), decode_times=False, decode_cf=True ).squeeze() 580 618 581 for var in d_SRF_beg.variables : 582 d_SRF_beg[var] = d_SRF_beg[var].where ( d_SRF_beg[var]<1.e20, 0.) 583 d_SRF_end[var] = d_SRF_end[var].where ( d_SRF_end[var]<1.e20, 0.) 584 585 if Routing == 'SIMPLE' : 586 d_RUN_beg = xr.open_dataset ( os.path.join (FileDir, file_RUN_beg), decode_times=False, decode_cf=True ).squeeze() 587 d_RUN_end = xr.open_dataset ( os.path.join (FileDir, file_RUN_end), decode_times=False, decode_cf=True ).squeeze() 588 619 if SRF : 620 for var in d_SRF_beg.variables : 621 d_SRF_beg[var] = d_SRF_beg[var].where ( d_SRF_beg[var]<1.e20, 0.) 622 d_SRF_end[var] = d_SRF_end[var].where ( d_SRF_end[var]<1.e20, 0.) 623 624 if Routing == 'SIMPLE' : 625 d_RUN_beg = xr.open_dataset ( os.path.join (FileDir, file_RUN_beg), decode_times=False, decode_cf=True ).squeeze() 626 d_RUN_end = xr.open_dataset ( os.path.join (FileDir, file_RUN_end), decode_times=False, decode_cf=True ).squeeze() 627 628 @Timer 589 629 def to_cell ( dd, newname='cell' ) : 590 '''Set space dimension to 'cell' ''' 630 '''Set space dimension to newname 631 ''' 591 632 for oldname in [ 'cell_mesh', 'y', 'points_physiques' ] : 592 try : dd = dd.rename ( {oldname : newname} )593 except : pass633 if oldname in dd.dims and oldname != newname : 634 dd = dd.rename ( {oldname : newname} ) 594 635 return dd 595 636 596 637 d_ATM_beg = to_cell ( d_ATM_beg ) 597 638 d_ATM_end = to_cell ( d_ATM_end ) 598 d_SRF_beg = to_cell ( d_SRF_beg ) 599 d_SRF_end = to_cell ( d_SRF_end ) 639 if SRF : 640 d_SRF_beg = to_cell ( d_SRF_beg ) 641 d_SRF_end = to_cell ( d_SRF_end ) 600 642 d_DYN_beg = to_cell ( d_DYN_beg ) 601 643 d_DYN_end = to_cell ( d_DYN_end ) 602 644 603 if Routing == 'SIMPLE' :645 if SRF and Routing == 'SIMPLE' : 604 646 d_RUN_beg = to_cell ( d_RUN_beg ) 605 647 d_RUN_end = to_cell ( d_RUN_end ) 606 648 607 649 d_ATM_his = to_cell ( d_ATM_his ) 608 d_SRF_his = to_cell ( d_SRF_his )609 650 if SRF : d_SRF_his = to_cell ( d_SRF_his ) 651 610 652 echo ( f'{file_ATM_beg = }' ) 611 653 echo ( f'{file_ATM_end = }' ) 612 654 echo ( f'{file_DYN_beg = }' ) 613 655 echo ( f'{file_DYN_end = }' ) 614 echo ( f'{file_SRF_beg = }' ) 615 echo ( f'{file_SRF_end = }' ) 616 if Routing == 'SIMPLE' : 617 echo ( f'{file_RUN_beg = }' ) 618 echo ( f'{file_RUN_end = }' ) 656 if SRF : 657 echo ( f'{file_SRF_beg = }' ) 658 echo ( f'{file_SRF_end = }' ) 659 if Routing == 'SIMPLE' : 660 echo ( f'{file_RUN_beg = }' ) 661 echo ( f'{file_RUN_end = }' ) 619 662 620 663 ## Write the full configuration 621 config_out = open (FullIniFile, 'w' )664 config_out = open (FullIniFile, 'w', encoding = 'utf-8') 622 665 config.write ( config_out ) 623 666 config_out.close () … … 626 669 if LMDZ : 627 670 echo ('ATM grid with cell surfaces : LMDZ') 628 ATM_lat = lmdz.geo2point ( rprec (d_ATM_his ['lat'])+0*rprec (d_ATM_his ['lon']), dim1D='cell' ) 629 ATM_lon = lmdz.geo2point ( 0*rprec (d_ATM_his ['lat'])+ rprec (d_ATM_his ['lon']), dim1D='cell' ) 630 ATM_aire = lmdz.geo2point ( rprec (d_ATM_his ['aire'] [0]), cumulPoles=True, dim1D='cell' ) 631 ATM_fter = lmdz.geo2point ( rprec (d_ATM_his ['fract_ter'][0]), dim1D='cell' ) 632 ATM_foce = lmdz.geo2point ( rprec (d_ATM_his ['fract_oce'][0]), dim1D='cell' ) 633 ATM_fsic = lmdz.geo2point ( rprec (d_ATM_his ['fract_sic'][0]), dim1D='cell' ) 634 ATM_flic = lmdz.geo2point ( rprec (d_ATM_his ['fract_lic'][0]), dim1D='cell' ) 635 SRF_lat = lmdz.geo2point ( rprec (d_SRF_his ['lat'])+0*rprec (d_SRF_his ['lon']), dim1D='cell' ) 636 SRF_lon = lmdz.geo2point ( 0*rprec (d_SRF_his ['lat'])+ rprec (d_SRF_his ['lon']), dim1D='cell' ) 637 SRF_aire = lmdz.geo2point ( rprec (d_SRF_his ['Areas']) * rprec (d_SRF_his ['Contfrac']), dim1D='cell', cumulPoles=True ) 638 SRF_areas = lmdz.geo2point ( rprec (d_SRF_his ['Areas']) , dim1D='cell', cumulPoles=True ) 639 SRF_contfrac = lmdz.geo2point ( rprec (d_SRF_his ['Contfrac']), dim1D='cell' ) 671 ATM_lat = lmdz.geo2point ( rprec (d_ATM_his ['lat'])+0*rprec (d_ATM_his ['lon']), dim1d='cell' ) 672 ATM_lon = lmdz.geo2point ( 0*rprec (d_ATM_his ['lat'])+ rprec (d_ATM_his ['lon']), dim1d='cell' ) 673 ATM_aire = lmdz.geo2point ( rprec (d_ATM_his ['aire'] [0]), cumul_poles=True, dim1d='cell' ) 674 ATM_fter = lmdz.geo2point ( rprec (d_ATM_his ['fract_ter'][0]), dim1d='cell' ) 675 ATM_foce = lmdz.geo2point ( rprec (d_ATM_his ['fract_oce'][0]), dim1d='cell' ) 676 ATM_fsic = lmdz.geo2point ( rprec (d_ATM_his ['fract_sic'][0]), dim1d='cell' ) 677 ATM_flic = lmdz.geo2point ( rprec (d_ATM_his ['fract_lic'][0]), dim1d='cell' ) 678 if SRF : 679 SRF_lat = lmdz.geo2point ( rprec (d_SRF_his ['lat'])+0*rprec (d_SRF_his ['lon']), dim1d='cell' ) 680 SRF_lon = lmdz.geo2point ( 0*rprec (d_SRF_his ['lat'])+ rprec (d_SRF_his ['lon']), dim1d='cell' ) 681 SRF_aire = lmdz.geo2point ( rprec (d_SRF_his ['Areas']) * rprec (d_SRF_his ['Contfrac']), dim1d='cell', cumul_poles=True ) 682 SRF_areas = lmdz.geo2point ( rprec (d_SRF_his ['Areas']) , dim1d='cell', cumul_poles=True ) 683 SRF_contfrac = lmdz.geo2point ( rprec (d_SRF_his ['Contfrac']), dim1d='cell' ) 640 684 641 685 if ICO : … … 643 687 echo ( 'ATM areas and fractions on latlon grid' ) 644 688 if 'lat_dom_out' in d_ATM_his.variables : 645 ATM_lat = lmdz.geo2point ( rprec (d_ATM_his ['lat_dom_out'])+0*rprec (d_ATM_his ['lon_dom_out']), dim1 D='cell' )646 ATM_lon = lmdz.geo2point ( 0*rprec (d_ATM_his ['lat_dom_out'])+ rprec (d_ATM_his ['lon_dom_out']), dim1 D='cell' )689 ATM_lat = lmdz.geo2point ( rprec (d_ATM_his ['lat_dom_out'])+0*rprec (d_ATM_his ['lon_dom_out']), dim1d='cell' ) 690 ATM_lon = lmdz.geo2point ( 0*rprec (d_ATM_his ['lat_dom_out'])+ rprec (d_ATM_his ['lon_dom_out']), dim1d='cell' ) 647 691 else : 648 ATM_lat = lmdz.geo2point ( rprec (d_ATM_his ['lat'])+0*rprec (d_ATM_his ['lon']), dim1 D='cell' )649 ATM_lon = lmdz.geo2point ( 0*rprec (d_ATM_his ['lat'])+ rprec (d_ATM_his ['lon']), dim1 D='cell' )650 ATM_aire = lmdz.geo2point ( rprec (d_ATM_his ['aire'][0]).squeeze(), cumul Poles=True, dim1D='cell' )651 ATM_fter = lmdz.geo2point ( rprec (d_ATM_his ['fract_ter'][0]), dim1 D='cell' )652 ATM_foce = lmdz.geo2point ( rprec (d_ATM_his ['fract_oce'][0]), dim1 D='cell' )653 ATM_fsic = lmdz.geo2point ( rprec (d_ATM_his ['fract_sic'][0]), dim1 D='cell' )654 ATM_flic = lmdz.geo2point ( rprec (d_ATM_his ['fract_lic'][0]), dim1 D='cell' )692 ATM_lat = lmdz.geo2point ( rprec (d_ATM_his ['lat'])+0*rprec (d_ATM_his ['lon']), dim1d='cell' ) 693 ATM_lon = lmdz.geo2point ( 0*rprec (d_ATM_his ['lat'])+ rprec (d_ATM_his ['lon']), dim1d='cell' ) 694 ATM_aire = lmdz.geo2point ( rprec (d_ATM_his ['aire'][0]).squeeze(), cumul_poles=True, dim1d='cell' ) 695 ATM_fter = lmdz.geo2point ( rprec (d_ATM_his ['fract_ter'][0]), dim1d='cell' ) 696 ATM_foce = lmdz.geo2point ( rprec (d_ATM_his ['fract_oce'][0]), dim1d='cell' ) 697 ATM_fsic = lmdz.geo2point ( rprec (d_ATM_his ['fract_sic'][0]), dim1d='cell' ) 698 ATM_flic = lmdz.geo2point ( rprec (d_ATM_his ['fract_lic'][0]), dim1d='cell' ) 655 699 656 700 if ATM_HIS == 'ico' : … … 664 708 ATM_flic = rprec (d_ATM_his ['fract_lic'][0]) 665 709 666 if SRF_HIS == 'latlon' : 667 echo ( 'SRF areas and fractions on latlon grid' ) 668 if 'lat_domain_landpoints_out' in d_SRF_his : 669 SRF_lat = lmdz.geo2point ( rprec (d_SRF_his ['lat_domain_landpoints_out'])+0*rprec (d_SRF_his ['lon_domain_landpoints_out']), dim1D='cell' ) 670 SRF_lon = lmdz.geo2point ( 0*rprec (d_SRF_his ['lat_domain_landpoints_out'])+ rprec (d_SRF_his ['lon_domain_landpoints_out']), dim1D='cell' ) 671 else : 672 if 'lat_domain_landpoints_out' in d_SRF_his : 673 SRF_lat = lmdz.geo2point ( rprec (d_SRF_his ['lat_dom_out'])+0*rprec (d_SRF_his ['lon_dom_out']), dim1D='cell' ) 674 SRF_lon = lmdz.geo2point ( 0*rprec (d_SRF_his ['lat_dom_out'])+ rprec (d_SRF_his ['lon_dom_out']), dim1D='cell' ) 710 if SRF : 711 if SRF_HIS == 'latlon' : 712 echo ( 'SRF areas and fractions on latlon grid' ) 713 if 'lat_domain_landpoints_out' in d_SRF_his : 714 SRF_lat = lmdz.geo2point ( rprec (d_SRF_his ['lat_domain_landpoints_out'])+0*rprec (d_SRF_his ['lon_domain_landpoints_out']), dim1d='cell' ) 715 SRF_lon = lmdz.geo2point ( 0*rprec (d_SRF_his ['lat_domain_landpoints_out'])+ rprec (d_SRF_his ['lon_domain_landpoints_out']), dim1d='cell' ) 675 716 else : 676 SRF_lat = lmdz.geo2point ( rprec (d_SRF_his ['lat'])+0*rprec (d_SRF_his ['lon']), dim1D='cell' ) 677 SRF_lon = lmdz.geo2point ( 0*rprec (d_SRF_his ['lat'])+ rprec (d_SRF_his ['lon']), dim1D='cell' ) 678 679 SRF_areas = lmdz.geo2point ( rprec (d_SRF_his ['Areas'] ) , dim1D='cell', cumulPoles=True ) 680 SRF_areafrac = lmdz.geo2point ( rprec (d_SRF_his ['AreaFrac']) , dim1D='cell', cumulPoles=True ) 681 SRF_contfrac = lmdz.geo2point ( rprec (d_SRF_his ['Contfrac']) , dim1D='cell', cumulPoles=True ) 682 SRF_aire = SRF_areafrac 683 684 if SRF_HIS == 'ico' : 685 echo ( 'SRF areas and fractions on latlon grid' ) 686 SRF_lat = rprec (d_SRF_his ['lat'] ) 687 SRF_lon = rprec (d_SRF_his ['lon'] ) 688 SRF_areas = rprec (d_SRF_his ['Areas'] ) 689 SRF_contfrac = rprec (d_SRF_his ['Contfrac']) 690 SRF_aire = SRF_areas * SRF_contfrac 717 if 'lat_domain_landpoints_out' in d_SRF_his : 718 SRF_lat = lmdz.geo2point ( rprec (d_SRF_his ['lat_dom_out'])+0*rprec (d_SRF_his ['lon_dom_out']), dim1d='cell' ) 719 SRF_lon = lmdz.geo2point ( 0*rprec (d_SRF_his ['lat_dom_out'])+ rprec (d_SRF_his ['lon_dom_out']), dim1d='cell' ) 720 else : 721 SRF_lat = lmdz.geo2point ( rprec (d_SRF_his ['lat'])+0*rprec (d_SRF_his ['lon']), dim1d='cell' ) 722 SRF_lon = lmdz.geo2point ( 0*rprec (d_SRF_his ['lat'])+ rprec (d_SRF_his ['lon']), dim1d='cell' ) 723 724 SRF_areas = lmdz.geo2point ( rprec (d_SRF_his ['Areas'] ) , dim1d='cell', cumul_poles=True ) 725 SRF_areafrac = lmdz.geo2point ( rprec (d_SRF_his ['AreaFrac']) , dim1d='cell', cumul_poles=True ) 726 SRF_contfrac = lmdz.geo2point ( rprec (d_SRF_his ['Contfrac']) , dim1d='cell', cumul_poles=True ) 727 SRF_aire = SRF_areafrac 728 729 if SRF_HIS == 'ico' : 730 echo ( 'SRF areas and fractions on latlon grid' ) 731 SRF_lat = rprec (d_SRF_his ['lat'] ) 732 SRF_lon = rprec (d_SRF_his ['lon'] ) 733 SRF_areas = rprec (d_SRF_his ['Areas'] ) 734 SRF_contfrac = rprec (d_SRF_his ['Contfrac']) 735 SRF_aire = SRF_areas * SRF_contfrac 691 736 692 737 ATM_fsea = ATM_foce + ATM_fsic … … 702 747 703 748 ## Write the full configuration 704 config_out = open (FullIniFile, 'w' )749 config_out = open (FullIniFile, 'w', encoding='utf-8') 705 750 config.write ( config_out ) 706 751 config_out.close () 707 752 708 753 if ICO : 709 754 # Area on icosahedron grid 710 755 d_DYN_aire = xr.open_dataset ( file_DYN_aire, decode_times=False ).squeeze() 711 756 712 if SortIco : 757 if SortIco : 713 758 # Creation d'une clef de tri pour le fichier aire 714 759 DYN_aire_keysort = np.lexsort ( (d_DYN_aire['lat'], d_DYN_aire['lon']) ) 715 else : 760 else : 716 761 DYN_aire_keysort = np.arange ( len ( d_DYN_aire['lat'] ) ) 717 762 718 763 DYN_lat = d_DYN_aire['lat'] 719 764 DYN_lon = d_DYN_aire['lon'] … … 725 770 DYN_flic = d_ATM_beg['FLIC'] 726 771 DYN_aire_fter = DYN_aire * DYN_fter 727 772 728 773 if LMDZ : 729 774 # Area on lon/lat grid … … 736 781 737 782 # Functions computing integrals and sum 783 @Timer 738 784 def DYN_stock_int (stock) : 739 785 '''Integrate (* surface) stock on atmosphere grid''' 740 DYN_stock_int = wu.Psum ( (stock * DYN_aire).to_masked_array().ravel() )741 return DYN_stock_int 742 786 return wu.Psum ( (stock * DYN_aire).to_masked_array().ravel() ) 787 788 @Timer 743 789 def ATM_flux_int (flux) : 744 790 '''Integrate (* time * surface) flux on atmosphere grid''' 745 ATM_flux_int =wu.Psum ( (flux * dtime_per_sec * ATM_aire).to_masked_array().ravel() )746 return ATM_flux_int 747 791 return wu.Psum ( (flux * dtime_per_sec * ATM_aire).to_masked_array().ravel() ) 792 793 @Timer 748 794 def LIC_flux_int (flux) : 749 795 '''Integrate (* time * surface) flux on land ice grid''' 750 LIC_flux_int = wu.Psum ( (flux * dtime_per_sec * ATM_aire_flic).to_masked_array().ravel() ) 751 return LIC_flux_int 752 753 def SRF_stock_int (stock) : 754 '''Integrate (* surface) stock on land grid''' 755 SRF_stock_int = wu.Ksum ( ( (stock * DYN_aire_fter).to_masked_array().ravel()) ) 756 return SRF_stock_int 757 758 def SRF_flux_int (flux) : 759 '''Integrate (* time * surface) flux on land grid''' 760 SRF_flux_int = wu.Psum ( (flux * dtime_per_sec * SRF_aire).to_masked_array().ravel() ) 761 return SRF_flux_int 762 796 return wu.Psum ( (flux * dtime_per_sec * ATM_aire_flic).to_masked_array().ravel() ) 797 798 if SRF : 799 @Timer 800 def SRF_stock_int (stock) : 801 '''Integrate (* surface) stock on land grid''' 802 return wu.Ksum ( ( (stock * DYN_aire_fter).to_masked_array().ravel()) ) 803 804 @Timer 805 def SRF_flux_int (flux) : 806 '''Integrate (* time * surface) flux on land grid''' 807 return wu.Psum ( (flux * dtime_per_sec * SRF_aire).to_masked_array().ravel() ) 808 809 @Timer 763 810 def ONE_stock_int (stock) : 764 811 '''Sum stock ''' 765 ONE_stock_int =wu.Psum ( stock.to_masked_array().ravel() )766 return ONE_stock_int 767 812 return wu.Psum ( stock.to_masked_array().ravel() ) 813 814 @Timer 768 815 def ONE_flux_int (flux) : 769 816 '''Integrate (* time) flux on area=1 grid ''' 770 ONE_flux_int =wu.Psum ( (flux * dtime_per_sec ).to_masked_array().ravel() )771 return ONE_flux_int 772 817 return wu.Psum ( (flux * dtime_per_sec ).to_masked_array().ravel() ) 818 819 @Timer 773 820 def Sprec ( tlist ) : 774 821 '''Accurate sum of list of scalar elements''' … … 783 830 784 831 DYN_aire_tot = ONE_stock_int ( DYN_aire ) 785 SRF_aire_tot = ONE_stock_int ( SRF_aire )832 if SRF : SRF_aire_tot = ONE_stock_int ( SRF_aire ) 786 833 787 834 echo ('') … … 790 837 echo ( f'ATM HIS : Area of ter in atmosphere : {ATM_aire_ter_tot:18.9e}' ) 791 838 echo ( f'ATM HIS : Area of lic in atmosphere : {ATM_aire_lic_tot:18.9e}' ) 792 echo ( f'ATM SRF : Area of atmosphere : {SRF_aire_tot:18.9e}' ) 839 if SRF : 840 echo ( f'ATM SRF : Area of atmosphere : {SRF_aire_tot:18.9e}' ) 793 841 echo ('') 794 echo ( 'ATM DYN : Area of atmosphere/(4pi R^2) : {:18.9f}'.format(DYN_aire_tot/(Ra*Ra*4*np.pi) ) ) 795 echo ( 'ATM HIS : Area of atmosphere/(4pi R^2) : {:18.9f}'.format(ATM_aire_tot/(Ra*Ra*4*np.pi) ) ) 796 echo ( 'ATM HIS : Area of ter in atmosphere/(4pi R^2) : {:18.9f}'.format(ATM_aire_ter_tot/(Ra*Ra*4*np.pi) ) ) 797 echo ( 'ATM SRF : Area of atmosphere/(4pi R^2) : {:18.9f}'.format(SRF_aire_tot/(Ra*Ra*4*np.pi) ) ) 798 echo ('') 799 echo ( f'ATM SRF : Area of atmosphere (no contfrac): {ONE_stock_int (SRF_areas):18.9e}' ) 800 801 802 if ( np.abs (ATM_aire_tot/(Ra*Ra*4*np.pi) - 1.0) > 0.01 ) : 803 raise Exception ('Error of atmosphere surface interpolated on lon/lat grid') 842 echo ( 'ATM DYN : Area of atmosphere/(4pi R^2) : {:18.9f}'.format(DYN_aire_tot/(RA*RA*4*np.pi) ) ) 843 echo ( 'ATM HIS : Area of atmosphere/(4pi R^2) : {:18.9f}'.format(ATM_aire_tot/(RA*RA*4*np.pi) ) ) 844 echo ( 'ATM HIS : Area of ter in atmosphere/(4pi R^2) : {:18.9f}'.format(ATM_aire_ter_tot/(RA*RA*4*np.pi) ) ) 845 if SRF : 846 echo ( 'ATM SRF : Area of atmosphere/(4pi R^2) : {:18.9f}'.format(SRF_aire_tot/(RA*RA*4*np.pi) ) ) 847 echo ('') 848 echo ( f'ATM SRF : Area of atmosphere (no contfrac): {ONE_stock_int (SRF_areas):18.9e}' ) 849 850 851 if np.abs (ATM_aire_tot/(RA*RA*4*np.pi) - 1.0) > 0.01 : 852 raise RuntimeError ('Error of atmosphere surface interpolated on lon/lat grid') 804 853 805 854 echo ( '\n====================================================================================' ) … … 817 866 DYN_psol_beg = d_DYN_beg['ps'] 818 867 DYN_psol_end = d_DYN_end['ps'] 819 if LMDZ : 820 DYN_psol_beg = lmdz.geo2point ( d_DYN_beg['ps'].isel(rlonv=slice(0,-1)), dim1 D='cell' )821 DYN_psol_end = lmdz.geo2point ( d_DYN_end['ps'].isel(rlonv=slice(0,-1)), dim1 D='cell' )822 868 if LMDZ : 869 DYN_psol_beg = lmdz.geo2point ( d_DYN_beg['ps'].isel(rlonv=slice(0,-1)), dim1d='cell' ) 870 DYN_psol_end = lmdz.geo2point ( d_DYN_end['ps'].isel(rlonv=slice(0,-1)), dim1d='cell' ) 871 823 872 echo ( '3D Pressure at the interface layers (not scalar points)' ) 824 873 DYN_pres_beg = ATM_Ahyb + ATM_Bhyb * DYN_psol_beg … … 848 897 echo ( f'(DYN_pres_end[-1]).min().item() = {ind[6]}' ) 849 898 echo ( f'(DYN_pres_end[-1]).max().item() = {ind[7]}' ) 850 raise Exception851 899 raise RuntimeError 900 852 901 klevp1 = ATM_Bhyb.shape[-1] 853 902 cell = DYN_psol_beg.shape[-1] … … 857 906 DYN_mass_beg = xr.DataArray ( np.empty( (klev, cell)), dims = ('sigs', 'cell'), coords=(np.arange(klev), np.arange(cell) ) ) 858 907 DYN_mass_end = xr.DataArray ( np.empty( (klev, cell)), dims = ('sigs', 'cell'), coords=(np.arange(klev), np.arange(cell) ) ) 859 908 860 909 for k in np.arange (klev) : 861 DYN_mass_beg[k,:] = ( DYN_pres_beg[k,:] - DYN_pres_beg[k+1,:] ) / G rav862 DYN_mass_end[k,:] = ( DYN_pres_end[k,:] - DYN_pres_end[k+1,:] ) / G rav910 DYN_mass_beg[k,:] = ( DYN_pres_beg[k,:] - DYN_pres_beg[k+1,:] ) / GRAV 911 DYN_mass_end[k,:] = ( DYN_pres_end[k,:] - DYN_pres_end[k+1,:] ) / GRAV 863 912 864 913 DYN_mass_beg_2D = DYN_mass_beg.sum (dim='sigs') … … 872 921 if 'H2Ov' in d_DYN_beg.variables : 873 922 echo ('reading LATLON : H2Ov, H2Ol, H2Oi' ) 874 DYN_wat_beg = lmdz.geo3point ( d_DYN_beg['H2Ov'] + d_DYN_beg['H2Ol'] + d_DYN_beg['H2Oi'].isel(rlonv=slice(0,-1) ), dim1 D='cell' )875 DYN_wat_end = lmdz.geo3point ( d_DYN_end['H2Ov'] + d_DYN_end['H2Ol'] + d_DYN_end['H2Oi'].isel(rlonv=slice(0,-1) ), dim1 D='cell' )923 DYN_wat_beg = lmdz.geo3point ( d_DYN_beg['H2Ov'] + d_DYN_beg['H2Ol'] + d_DYN_beg['H2Oi'].isel(rlonv=slice(0,-1) ), dim1d='cell' ) 924 DYN_wat_end = lmdz.geo3point ( d_DYN_end['H2Ov'] + d_DYN_end['H2Ol'] + d_DYN_end['H2Oi'].isel(rlonv=slice(0,-1) ), dim1d='cell' ) 876 925 if 'H2Ov_g' in d_DYN_beg.variables : 877 926 echo ('reading LATLON : H2O_g, H2O_l, H2O_s' ) 878 DYN_wat_beg = lmdz.geo3point ( (d_DYN_beg['H2O_g'] + d_DYN_beg['H2O_l'] + d_DYN_beg['H2O_s']).isel(rlonv=slice(0,-1) ), dim1 D='cell' )879 DYN_wat_end = lmdz.geo3point ( (d_DYN_end['H2O_g'] + d_DYN_end['H2O_l'] + d_DYN_end['H2O_s']).isel(rlonv=slice(0,-1) ), dim1 D='cell' )927 DYN_wat_beg = lmdz.geo3point ( (d_DYN_beg['H2O_g'] + d_DYN_beg['H2O_l'] + d_DYN_beg['H2O_s']).isel(rlonv=slice(0,-1) ), dim1d='cell' ) 928 DYN_wat_end = lmdz.geo3point ( (d_DYN_end['H2O_g'] + d_DYN_end['H2O_l'] + d_DYN_end['H2O_s']).isel(rlonv=slice(0,-1) ), dim1d='cell' ) 880 929 if ICO : 881 930 if 'H2Ov_g' in d_DYN_beg.variables : 882 931 echo ('reading ICO : H2O_g, H2O_l, H2O_s' ) 883 DYN_wat_beg = (d_DYN_beg['H2O_g'] + d_DYN_beg['H2O_l'] + d_DYN_beg['H2O_s'])884 DYN_wat_end = (d_DYN_end['H2O_g'] + d_DYN_end['H2O_l'] + d_DYN_end['H2O_s'])932 DYN_wat_beg = d_DYN_beg['H2O_g'] + d_DYN_beg['H2O_l'] + d_DYN_beg['H2O_s'] 933 DYN_wat_end = d_DYN_end['H2O_g'] + d_DYN_end['H2O_l'] + d_DYN_end['H2O_s'] 885 934 elif 'H2O_g' in d_DYN_beg.variables : 886 935 echo ('reading ICO : H2O_g, H2O_l, H2O_s' ) 887 DYN_wat_beg = (d_DYN_beg['H2O_g'] + d_DYN_beg['H2O_l'] + d_DYN_beg['H2O_s'])888 DYN_wat_end = (d_DYN_end['H2O_g'] + d_DYN_end['H2O_l'] + d_DYN_end['H2O_s'])936 DYN_wat_beg = d_DYN_beg['H2O_g'] + d_DYN_beg['H2O_l'] + d_DYN_beg['H2O_s'] 937 DYN_wat_end = d_DYN_end['H2O_g'] + d_DYN_end['H2O_l'] + d_DYN_end['H2O_s'] 889 938 elif 'q' in d_DYN_beg.variables : 890 939 echo ('reading ICO : q' ) 891 DYN_wat_beg = (d_DYN_beg['q'].isel(nq=0) + d_DYN_beg['q'].isel(nq=1) + d_DYN_beg['q'].isel(nq=2))892 DYN_wat_end = (d_DYN_end['q'].isel(nq=0) + d_DYN_end['q'].isel(nq=1) + d_DYN_end['q'].isel(nq=2))893 894 if 'lev' in DYN_wat_beg.dims : 940 DYN_wat_beg = d_DYN_beg['q'].isel(nq=0) + d_DYN_beg['q'].isel(nq=1) + d_DYN_beg['q'].isel(nq=2) 941 DYN_wat_end = d_DYN_end['q'].isel(nq=0) + d_DYN_end['q'].isel(nq=1) + d_DYN_end['q'].isel(nq=2) 942 943 if 'lev' in DYN_wat_beg.dims : 895 944 DYN_wat_beg = DYN_wat_beg.rename ( {'lev':'sigs'} ) 896 945 DYN_wat_end = DYN_wat_end.rename ( {'lev':'sigs'} ) 897 946 898 947 echo ( 'Compute water content : vertical and horizontal integral' ) 899 948 … … 940 989 ATM_qs02_end = d_ATM_end['QS02'] * d_ATM_end['FLIC'] 941 990 ATM_qs03_end = d_ATM_end['QS03'] * d_ATM_end['FOCE'] 942 ATM_qs04_end = d_ATM_end['QS04'] * d_ATM_end['FSIC'] 943 944 if ICO : 945 ATM_sno_beg = ATM_sno_beg 946 ATM_sno_end = ATM_sno_end 947 ATM_qs_beg = ATM_qs_beg 948 ATM_qs_end = ATM_qs_end 949 ATM_qsol_beg = ATM_qsol_beg 950 ATM_qs01_beg = ATM_qs01_beg 951 ATM_qs02_beg = ATM_qs02_beg 952 ATM_qs03_beg = ATM_qs03_beg 953 ATM_qs04_beg = ATM_qs04_beg 954 ATM_qsol_end = ATM_qsol_end 955 ATM_qs01_end = ATM_qs01_end 956 ATM_qs02_end = ATM_qs02_end 957 ATM_qs03_end = ATM_qs03_end 958 ATM_qs04_end = ATM_qs04_end 959 LIC_sno_beg = LIC_sno_beg 960 LIC_sno_end = LIC_sno_end 961 LIC_runlic0_beg = LIC_runlic0_beg 962 LIC_runlic0_end = LIC_runlic0_end 963 991 ATM_qs04_end = d_ATM_end['QS04'] * d_ATM_end['FSIC'] 992 964 993 LIC_qs_beg = ATM_qs02_beg 965 994 LIC_qs_end = ATM_qs02_end … … 1020 1049 prtFlux ( 'dMass (eau + neige atm) ', dDYN_mas_wat + dATM_mas_sno , 'e', True) 1021 1050 1022 echo ( '\n====================================================================================' ) 1023 echo ( f'-- SRF changes -- {Title}' )1024 1025 if Routing == 'SIMPLE' : 1026 RUN_mas_wat_fast_beg = ONE_stock_int ( d_RUN_beg ['fast_reservoir'] )1027 RUN_mas_wat_slow_beg = ONE_stock_int ( d_RUN_beg ['slow_reservoir'] )1028 RUN_mas_wat_stream_beg = ONE_stock_int ( d_RUN_beg ['stream_reservoir'])1029 1030 RUN_mas_wat_flood_beg = ONE_stock_int ( d_SRF_beg ['floodres'] )1031 RUN_mas_wat_lake_beg = ONE_stock_int ( d_SRF_beg ['lakeres'] )1032 RUN_mas_wat_pond_beg = ONE_stock_int ( d_SRF_beg ['pondres'] )1033 1034 RUN_mas_wat_fast_end = ONE_stock_int ( d_RUN_end ['fast_reservoir'] )1035 RUN_mas_wat_slow_end = ONE_stock_int ( d_RUN_end ['slow_reservoir'] )1036 RUN_mas_wat_stream_end = ONE_stock_int ( d_RUN_end ['stream_reservoir'] )1037 1038 RUN_mas_wat_flood_end = ONE_stock_int ( d_SRF_end ['floodres'] )1039 RUN_mas_wat_lake_end = ONE_stock_int ( d_SRF_end ['lakeres'] )1040 RUN_mas_wat_pond_end = ONE_stock_int ( d_SRF_end ['pondres'] )1041 1042 if Routing == 'SECHIBA' :1043 RUN_mas_wat_fast_beg = ONE_stock_int ( d_SRF_beg ['fastres'] )1044 RUN_mas_wat_slow_beg = ONE_stock_int ( d_SRF_beg ['slowres'] )1045 RUN_mas_wat_stream_beg = ONE_stock_int ( d_SRF_beg ['streamres'] )1046 RUN_mas_wat_flood_beg = ONE_stock_int ( d_SRF_beg ['floodres'] )1047 RUN_mas_wat_lake_beg = ONE_stock_int ( d_SRF_beg ['lakeres'] )1048 RUN_mas_wat_pond_beg = ONE_stock_int ( d_SRF_beg ['pondres'] )1049 1050 RUN_mas_wat_fast_end = ONE_stock_int ( d_SRF_end ['fastres'] )1051 RUN_mas_wat_slow_end = ONE_stock_int ( d_SRF_end ['slowres'] )1052 RUN_mas_wat_stream_end = ONE_stock_int ( d_SRF_end ['streamres'] )1053 RUN_mas_wat_flood_end = ONE_stock_int ( d_SRF_end ['floodres'] )1054 RUN_mas_wat_lake_end = ONE_stock_int ( d_SRF_end ['lakeres'] )1055 RUN_mas_wat_pond_end = ONE_stock_int ( d_SRF_end ['pondres'] )1056 1057 RUN_mas_wat_beg = Sprec ( [RUN_mas_wat_fast_beg , RUN_mas_wat_slow_beg, RUN_mas_wat_stream_beg,1058 RUN_mas_wat_flood_beg, RUN_mas_wat_lake_beg, RUN_mas_wat_pond_beg] )1059 1060 RUN_mas_wat_end = Sprec ( [RUN_mas_wat_fast_end , RUN_mas_wat_slow_end , RUN_mas_wat_stream_end,1061 RUN_mas_wat_flood_end , RUN_mas_wat_lake_end , RUN_mas_wat_pond_end] )1062 1063 dRUN_mas_wat_fast = RUN_mas_wat_fast_end - RUN_mas_wat_fast_beg1064 dRUN_mas_wat_slow = RUN_mas_wat_slow_end - RUN_mas_wat_slow_beg1065 dRUN_mas_wat_stream = RUN_mas_wat_stream_end - RUN_mas_wat_stream_beg1066 dRUN_mas_wat_flood = RUN_mas_wat_flood_end - RUN_mas_wat_flood_beg1067 dRUN_mas_wat_lake = RUN_mas_wat_lake_end - RUN_mas_wat_lake_beg1068 dRUN_mas_wat_pond = RUN_mas_wat_pond_end - RUN_mas_wat_pond_beg1069 1070 dRUN_mas_wat = RUN_mas_wat_end - RUN_mas_wat_beg1071 1072 echo ( f'\nRunoff reservoirs -- {Title} ')1073 echo ( f'------------------------------------------------------------------------------------' )1074 echo ( f'RUN_mas_wat_fast_beg = {RUN_mas_wat_fast_beg :12.6e} kg | RUN_mas_wat_fast_end = {RUN_mas_wat_fast_end :12.6e} kg ' )1075 echo ( f'RUN_mas_wat_slow_beg = {RUN_mas_wat_slow_beg :12.6e} kg | RUN_mas_wat_slow_end = {RUN_mas_wat_slow_end :12.6e} kg ' )1076 echo ( f'RUN_mas_wat_stream_beg = {RUN_mas_wat_stream_beg:12.6e} kg | RUN_mas_wat_stream_end = {RUN_mas_wat_stream_end:12.6e} kg ' )1077 echo ( f'RUN_mas_wat_flood_beg = {RUN_mas_wat_flood_beg :12.6e} kg | RUN_mas_wat_flood_end = {RUN_mas_wat_flood_end :12.6e} kg ' )1078 echo ( f'RUN_mas_wat_lake_beg = {RUN_mas_wat_lake_beg :12.6e} kg | RUN_mas_wat_lake_end = {RUN_mas_wat_lake_end :12.6e} kg ' )1079 echo ( f'RUN_mas_wat_pond_beg = {RUN_mas_wat_pond_beg :12.6e} kg | RUN_mas_wat_pond_end = {RUN_mas_wat_pond_end :12.6e} kg ' )1080 echo ( f'RUN_mas_wat_beg = {RUN_mas_wat_beg :12.6e} kg | RUN_mas_wat_end = {RUN_mas_wat_end :12.6e} kg ' )1081 1082 echo ( '------------------------------------------------------------------------------------' )1083 1084 prtFlux ( 'dMass (fast) ', dRUN_mas_wat_fast , 'e', True )1085 prtFlux ( 'dMass (slow) ', dRUN_mas_wat_slow , 'e', True )1086 prtFlux ( 'dMass (stream) ', dRUN_mas_wat_stream, 'e', True )1087 prtFlux ( 'dMass (flood) ', dRUN_mas_wat_flood , 'e', True )1088 prtFlux ( 'dMass (lake) ', dRUN_mas_wat_lake , 'e', True )1089 prtFlux ( 'dMass (pond) ', dRUN_mas_wat_pond , 'e', True )1090 prtFlux ( 'dMass (all) ', dRUN_mas_wat , 'e', True )1091 1092 echo ( f'\nWater content in routing -- {Title} ' )1093 echo ( '------------------------------------------------------------------------------------' )1094 echo ( f'RUN_mas_wat_beg = {RUN_mas_wat_end:12.6e} kg | RUN_mas_wat_end = {RUN_mas_wat_end:12.6e} kg' )1095 prtFlux ( 'dMass (routing) ', dRUN_mas_wat , 'e', True )1096 1097 echo ( '\n====================================================================================' )1098 print (f'Reading SRF restart')1099 SRF_tot_watveg_beg = d_SRF_beg['tot_watveg_beg'] ; SRF_tot_watveg_beg = SRF_tot_watveg_beg .where (SRF_tot_watveg_beg < 1E15, 0.)1100 SRF_tot_watsoil_beg = d_SRF_beg['tot_watsoil_beg'] ; SRF_tot_watsoil_beg = SRF_tot_watsoil_beg.where (SRF_tot_watsoil_beg < 1E15, 0.)1101 SRF_snow_beg = d_SRF_beg['snow_beg'] ; SRF_snow_beg = SRF_snow_beg .where (SRF_snow_beg < 1E15, 0.)1102 SRF_lakeres_beg = d_SRF_beg['lakeres'] ; SRF_lakeres_beg = SRF_lakeres_beg .where (SRF_lakeres_beg < 1E15, 0.)1103 1104 SRF_tot_watveg_end = d_SRF_end['tot_watveg_beg'] ; SRF_tot_watveg_end = SRF_tot_watveg_end .where (SRF_tot_watveg_end < 1E15, 0.)1105 SRF_tot_watsoil_end = d_SRF_end['tot_watsoil_beg'] ; SRF_tot_watsoil_end = SRF_tot_watsoil_end.where (SRF_tot_watsoil_end < 1E15, 0.)1106 SRF_snow_end = d_SRF_end['snow_beg'] ; SRF_snow_end = SRF_snow_end .where (SRF_snow_end < 1E15, 0.)1107 SRF_lakeres_end = d_SRF_end['lakeres'] ; SRF_lakeres_end = SRF_lakeres_end .where (SRF_lakeres_end < 1E15, 0.)1108 1109 if LMDZ :1110 SRF_tot_watveg_beg = lmdz.geo2point (SRF_tot_watveg_beg , dim1D='cell')1111 SRF_tot_watsoil_beg = lmdz.geo2point (SRF_tot_watsoil_beg, dim1D='cell')1112 SRF_snow_beg = lmdz.geo2point (SRF_snow_beg , dim1D='cell')1113 SRF_lakeres_beg = lmdz.geo2point (SRF_lakeres_beg , dim1D='cell')1114 SRF_tot_watveg_end = lmdz.geo2point (SRF_tot_watveg_end , dim1D='cell')1115 SRF_tot_watsoil_end = lmdz.geo2point (SRF_tot_watsoil_end, dim1D='cell')1116 SRF_snow_end = lmdz.geo2point (SRF_snow_end , dim1D='cell')1117 SRF_lakeres_end = lmdz.geo2point (SRF_lakeres_end , dim1D='cell')1118 1119 1120 # Stock dSoilHum dInterce dSWE dStream dFastR dSlowR dLake dPond dFlood1121 1122 SRF_wat_beg = SRF_tot_watveg_beg + SRF_tot_watsoil_beg + SRF_snow_beg1123 SRF_wat_end = SRF_tot_watveg_end + SRF_tot_watsoil_end + SRF_snow_end1124 1125 echo ( '\n====================================================================================' )1126 print ('Computing integrals')1127 1128 print ( ' 1/8', end='' ) ; sys.stdout.flush ()1129 SRF_mas_watveg_beg = SRF_stock_int ( SRF_tot_watveg_beg )1130 print ( ' 2/8', end='' ) ; sys.stdout.flush ()1131 SRF_mas_watsoil_beg = SRF_stock_int ( SRF_tot_watsoil_beg )1132 print ( ' 3/8', end='' ) ; sys.stdout.flush ()1133 SRF_mas_snow_beg = SRF_stock_int ( SRF_snow_beg )1134 print ( ' 4/8', end='' ) ; sys.stdout.flush ()1135 SRF_mas_lake_beg = ONE_stock_int ( SRF_lakeres_beg )1136 print ( ' 5/8', end='' ) ; sys.stdout.flush ()1137 1138 SRF_mas_watveg_end = SRF_stock_int ( SRF_tot_watveg_end )1139 print ( ' 6/8', end='' ) ; sys.stdout.flush ()1140 SRF_mas_watsoil_end = SRF_stock_int ( SRF_tot_watsoil_end )1141 print ( ' 7/8', end='' ) ; sys.stdout.flush ()1142 SRF_mas_snow_end = SRF_stock_int ( SRF_snow_end )1143 print ( ' 8/8', end='' ) ; sys.stdout.flush ()1144 SRF_mas_lake_end = ONE_stock_int ( SRF_lakeres_end )1145 1146 print (' -- ') ; sys.stdout.flush ()1147 1148 dSRF_mas_watveg = Sprec ( [SRF_mas_watveg_end , -SRF_mas_watveg_beg] )1149 dSRF_mas_watsoil = Sprec ( [SRF_mas_watsoil_end, -SRF_mas_watsoil_beg] )1150 dSRF_mas_snow = Sprec ( [SRF_mas_snow_end , -SRF_mas_snow_beg] )1151 dSRF_mas_lake = Sprec ( [SRF_mas_lake_end , -SRF_mas_lake_beg] )1152 1153 echo ( '------------------------------------------------------------------------------------' )1154 echo ( f'\nSurface reservoirs -- {Title} ')1155 echo ( f'SRF_mas_watveg_beg = {SRF_mas_watveg_beg :12.6e} kg | SRF_mas_watveg_end = {SRF_mas_watveg_end :12.6e} kg ' )1156 echo ( f'SRF_mas_watsoil_beg = {SRF_mas_watsoil_beg:12.6e} kg | SRF_mas_watsoil_end = {SRF_mas_watsoil_end:12.6e} kg ' )1157 echo ( f'SRF_mas_snow_beg = {SRF_mas_snow_beg :12.6e} kg | SRF_mas_snow_end = {SRF_mas_snow_end :12.6e} kg ' )1158 echo ( f'SRF_mas_lake_beg = {SRF_mas_lake_beg :12.6e} kg | SRF_mas_lake_end = {SRF_mas_lake_end :12.6e} kg ' )1159 1160 prtFlux ( 'dMass (watveg) ', dSRF_mas_watveg , 'e' , True )1161 prtFlux ( 'dMass (watsoil)', dSRF_mas_watsoil, 'e' , True )1162 prtFlux ( 'dMass (snow) ', dSRF_mas_snow , 'e' , True )1163 prtFlux ( 'dMass (lake) ', dSRF_mas_lake , 'e' , True )1164 1165 SRF_mas_wat_beg = Sprec ( [SRF_mas_watveg_beg , SRF_mas_watsoil_beg, SRF_mas_snow_beg] )1166 SRF_mas_wat_end = Sprec ( [SRF_mas_watveg_end , SRF_mas_watsoil_end, SRF_mas_snow_end] )1167 1168 dSRF_mas_wat = Sprec ( [+SRF_mas_watveg_end , +SRF_mas_watsoil_end, +SRF_mas_snow_end, 1169 -SRF_mas_watveg_beg , -SRF_mas_watsoil_beg, -SRF_mas_snow_beg] )1170 1171 echo ( '------------------------------------------------------------------------------------' )1172 echo ( f'Water content in surface -- {Title} ' )1173 echo ( f'SRF_mas_wat_beg = {SRF_mas_wat_beg:12.6e} kg | SRF_mas_wat_end = {SRF_mas_wat_end:12.6e} kg ')1174 prtFlux ( 'dMass (water srf)', dSRF_mas_wat, 'e', True )1175 1176 echo ( '------------------------------------------------------------------------------------' )1177 echo ( 'Water content in ATM + SRF + RUN + LAKE' )1178 echo ( 'mas_wat_beg = {:12.6e} kg | mas_wat_end = {:12.6e} kg '.1179 format (DYN_mas_wat_beg + ATM_mas_sno_beg + RUN_mas_wat_beg + SRF_mas_wat_beg + SRF_mas_lake_beg ,1180 DYN_mas_wat_end + ATM_mas_sno_end + RUN_mas_wat_end + SRF_mas_wat_end + SRF_mas_lake_end ) )1181 prtFlux ( 'dMass (water atm+srf+run+lake)', dDYN_mas_wat + dATM_mas_sno + dRUN_mas_wat + dSRF_mas_wat + dSRF_mas_lake, 'e', True)1051 if SRF : 1052 echo ( '\n====================================================================================' ) 1053 echo ( f'-- SRF changes -- {Title} ' ) 1054 1055 if Routing == 'SIMPLE' : 1056 RUN_mas_wat_fast_beg = ONE_stock_int ( d_RUN_beg ['fast_reservoir'] ) 1057 RUN_mas_wat_slow_beg = ONE_stock_int ( d_RUN_beg ['slow_reservoir'] ) 1058 RUN_mas_wat_stream_beg = ONE_stock_int ( d_RUN_beg ['stream_reservoir'] ) 1059 RUN_mas_wat_flood_beg = ONE_stock_int ( d_SRF_beg ['floodres'] ) 1060 RUN_mas_wat_lake_beg = ONE_stock_int ( d_SRF_beg ['lakeres'] ) 1061 RUN_mas_wat_pond_beg = ONE_stock_int ( d_SRF_beg ['pondres'] ) 1062 1063 RUN_mas_wat_fast_end = ONE_stock_int ( d_RUN_end ['fast_reservoir'] ) 1064 RUN_mas_wat_slow_end = ONE_stock_int ( d_RUN_end ['slow_reservoir'] ) 1065 RUN_mas_wat_stream_end = ONE_stock_int ( d_RUN_end ['stream_reservoir'] ) 1066 1067 RUN_mas_wat_flood_end = ONE_stock_int ( d_SRF_end ['floodres'] ) 1068 RUN_mas_wat_lake_end = ONE_stock_int ( d_SRF_end ['lakeres'] ) 1069 RUN_mas_wat_pond_end = ONE_stock_int ( d_SRF_end ['pondres'] ) 1070 1071 if Routing == 'SECHIBA' : 1072 RUN_mas_wat_fast_beg = ONE_stock_int ( d_SRF_beg ['fastres'] ) 1073 RUN_mas_wat_slow_beg = ONE_stock_int ( d_SRF_beg ['slowres'] ) 1074 RUN_mas_wat_stream_beg = ONE_stock_int ( d_SRF_beg ['streamres'] ) 1075 RUN_mas_wat_flood_beg = ONE_stock_int ( d_SRF_beg ['floodres'] ) 1076 RUN_mas_wat_lake_beg = ONE_stock_int ( d_SRF_beg ['lakeres'] ) 1077 RUN_mas_wat_pond_beg = ONE_stock_int ( d_SRF_beg ['pondres'] ) 1078 1079 RUN_mas_wat_fast_end = ONE_stock_int ( d_SRF_end ['fastres'] ) 1080 RUN_mas_wat_slow_end = ONE_stock_int ( d_SRF_end ['slowres'] ) 1081 RUN_mas_wat_stream_end = ONE_stock_int ( d_SRF_end ['streamres'] ) 1082 RUN_mas_wat_flood_end = ONE_stock_int ( d_SRF_end ['floodres'] ) 1083 RUN_mas_wat_lake_end = ONE_stock_int ( d_SRF_end ['lakeres'] ) 1084 RUN_mas_wat_pond_end = ONE_stock_int ( d_SRF_end ['pondres'] ) 1085 1086 RUN_mas_wat_beg = Sprec ( [RUN_mas_wat_fast_beg , RUN_mas_wat_slow_beg, RUN_mas_wat_stream_beg, 1087 RUN_mas_wat_flood_beg, RUN_mas_wat_lake_beg, RUN_mas_wat_pond_beg] ) 1088 1089 RUN_mas_wat_end = Sprec ( [RUN_mas_wat_fast_end , RUN_mas_wat_slow_end , RUN_mas_wat_stream_end, 1090 RUN_mas_wat_flood_end , RUN_mas_wat_lake_end , RUN_mas_wat_pond_end] ) 1091 1092 dRUN_mas_wat_fast = RUN_mas_wat_fast_end - RUN_mas_wat_fast_beg 1093 dRUN_mas_wat_slow = RUN_mas_wat_slow_end - RUN_mas_wat_slow_beg 1094 dRUN_mas_wat_stream = RUN_mas_wat_stream_end - RUN_mas_wat_stream_beg 1095 dRUN_mas_wat_flood = RUN_mas_wat_flood_end - RUN_mas_wat_flood_beg 1096 dRUN_mas_wat_lake = RUN_mas_wat_lake_end - RUN_mas_wat_lake_beg 1097 dRUN_mas_wat_pond = RUN_mas_wat_pond_end - RUN_mas_wat_pond_beg 1098 1099 dRUN_mas_wat = RUN_mas_wat_end - RUN_mas_wat_beg 1100 1101 echo ( f'\nRunoff reservoirs -- {Title} ') 1102 echo ( '------------------------------------------------------------------------------------' ) 1103 echo ( f'RUN_mas_wat_fast_beg = {RUN_mas_wat_fast_beg :12.6e} kg | RUN_mas_wat_fast_end = {RUN_mas_wat_fast_end :12.6e} kg ' ) 1104 echo ( f'RUN_mas_wat_slow_beg = {RUN_mas_wat_slow_beg :12.6e} kg | RUN_mas_wat_slow_end = {RUN_mas_wat_slow_end :12.6e} kg ' ) 1105 echo ( f'RUN_mas_wat_stream_beg = {RUN_mas_wat_stream_beg:12.6e} kg | RUN_mas_wat_stream_end = {RUN_mas_wat_stream_end:12.6e} kg ' ) 1106 echo ( f'RUN_mas_wat_flood_beg = {RUN_mas_wat_flood_beg :12.6e} kg | RUN_mas_wat_flood_end = {RUN_mas_wat_flood_end :12.6e} kg ' ) 1107 echo ( f'RUN_mas_wat_lake_beg = {RUN_mas_wat_lake_beg :12.6e} kg | RUN_mas_wat_lake_end = {RUN_mas_wat_lake_end :12.6e} kg ' ) 1108 echo ( f'RUN_mas_wat_pond_beg = {RUN_mas_wat_pond_beg :12.6e} kg | RUN_mas_wat_pond_end = {RUN_mas_wat_pond_end :12.6e} kg ' ) 1109 echo ( f'RUN_mas_wat_beg = {RUN_mas_wat_beg :12.6e} kg | RUN_mas_wat_end = {RUN_mas_wat_end :12.6e} kg ' ) 1110 1111 echo ( '------------------------------------------------------------------------------------' ) 1112 1113 prtFlux ( 'dMass (fast) ', dRUN_mas_wat_fast , 'e', True ) 1114 prtFlux ( 'dMass (slow) ', dRUN_mas_wat_slow , 'e', True ) 1115 prtFlux ( 'dMass (stream) ', dRUN_mas_wat_stream, 'e', True ) 1116 prtFlux ( 'dMass (flood) ', dRUN_mas_wat_flood , 'e', True ) 1117 prtFlux ( 'dMass (lake) ', dRUN_mas_wat_lake , 'e', True ) 1118 prtFlux ( 'dMass (pond) ', dRUN_mas_wat_pond , 'e', True ) 1119 prtFlux ( 'dMass (all) ', dRUN_mas_wat , 'e', True ) 1120 1121 echo ( f'\nWater content in routing -- {Title} ' ) 1122 echo ( '------------------------------------------------------------------------------------' ) 1123 echo ( f'RUN_mas_wat_beg = {RUN_mas_wat_end:12.6e} kg | RUN_mas_wat_end = {RUN_mas_wat_end:12.6e} kg' ) 1124 prtFlux ( 'dMass (routing) ', dRUN_mas_wat , 'e', True ) 1125 1126 echo ( '\n====================================================================================' ) 1127 print ( 'Reading SRF restart') 1128 SRF_tot_watveg_beg = d_SRF_beg['tot_watveg_beg'] ; SRF_tot_watveg_beg = SRF_tot_watveg_beg .where (SRF_tot_watveg_beg < 1E15, 0.) 1129 SRF_tot_watsoil_beg = d_SRF_beg['tot_watsoil_beg'] ; SRF_tot_watsoil_beg = SRF_tot_watsoil_beg.where (SRF_tot_watsoil_beg < 1E15, 0.) 1130 SRF_snow_beg = d_SRF_beg['snow_beg'] ; SRF_snow_beg = SRF_snow_beg .where (SRF_snow_beg < 1E15, 0.) 1131 SRF_lakeres_beg = d_SRF_beg['lakeres'] ; SRF_lakeres_beg = SRF_lakeres_beg .where (SRF_lakeres_beg < 1E15, 0.) 1132 1133 SRF_tot_watveg_end = d_SRF_end['tot_watveg_beg'] ; SRF_tot_watveg_end = SRF_tot_watveg_end .where (SRF_tot_watveg_end < 1E15, 0.) 1134 SRF_tot_watsoil_end = d_SRF_end['tot_watsoil_beg'] ; SRF_tot_watsoil_end = SRF_tot_watsoil_end.where (SRF_tot_watsoil_end < 1E15, 0.) 1135 SRF_snow_end = d_SRF_end['snow_beg'] ; SRF_snow_end = SRF_snow_end .where (SRF_snow_end < 1E15, 0.) 1136 SRF_lakeres_end = d_SRF_end['lakeres'] ; SRF_lakeres_end = SRF_lakeres_end .where (SRF_lakeres_end < 1E15, 0.) 1137 1138 if LMDZ : 1139 SRF_tot_watveg_beg = lmdz.geo2point (SRF_tot_watveg_beg , dim1d='cell') 1140 SRF_tot_watsoil_beg = lmdz.geo2point (SRF_tot_watsoil_beg, dim1d='cell') 1141 SRF_snow_beg = lmdz.geo2point (SRF_snow_beg , dim1d='cell') 1142 SRF_lakeres_beg = lmdz.geo2point (SRF_lakeres_beg , dim1d='cell') 1143 SRF_tot_watveg_end = lmdz.geo2point (SRF_tot_watveg_end , dim1d='cell') 1144 SRF_tot_watsoil_end = lmdz.geo2point (SRF_tot_watsoil_end, dim1d='cell') 1145 SRF_snow_end = lmdz.geo2point (SRF_snow_end , dim1d='cell') 1146 SRF_lakeres_end = lmdz.geo2point (SRF_lakeres_end , dim1d='cell') 1147 1148 1149 # Stock dSoilHum dInterce dSWE dStream dFastR dSlowR dLake dPond dFlood 1150 1151 SRF_wat_beg = SRF_tot_watveg_beg + SRF_tot_watsoil_beg + SRF_snow_beg 1152 SRF_wat_end = SRF_tot_watveg_end + SRF_tot_watsoil_end + SRF_snow_end 1153 1154 echo ( '\n====================================================================================' ) 1155 print ('Computing integrals') 1156 1157 print ( ' 1/8', end='' ) ; sys.stdout.flush () 1158 SRF_mas_watveg_beg = SRF_stock_int ( SRF_tot_watveg_beg ) 1159 print ( ' 2/8', end='' ) ; sys.stdout.flush () 1160 SRF_mas_watsoil_beg = SRF_stock_int ( SRF_tot_watsoil_beg ) 1161 print ( ' 3/8', end='' ) ; sys.stdout.flush () 1162 SRF_mas_snow_beg = SRF_stock_int ( SRF_snow_beg ) 1163 print ( ' 4/8', end='' ) ; sys.stdout.flush () 1164 SRF_mas_lake_beg = ONE_stock_int ( SRF_lakeres_beg ) 1165 print ( ' 5/8', end='' ) ; sys.stdout.flush () 1166 1167 SRF_mas_watveg_end = SRF_stock_int ( SRF_tot_watveg_end ) 1168 print ( ' 6/8', end='' ) ; sys.stdout.flush () 1169 SRF_mas_watsoil_end = SRF_stock_int ( SRF_tot_watsoil_end ) 1170 print ( ' 7/8', end='' ) ; sys.stdout.flush () 1171 SRF_mas_snow_end = SRF_stock_int ( SRF_snow_end ) 1172 print ( ' 8/8', end='' ) ; sys.stdout.flush () 1173 SRF_mas_lake_end = ONE_stock_int ( SRF_lakeres_end ) 1174 1175 print (' -- ') ; sys.stdout.flush () 1176 1177 dSRF_mas_watveg = Sprec ( [SRF_mas_watveg_end , -SRF_mas_watveg_beg] ) 1178 dSRF_mas_watsoil = Sprec ( [SRF_mas_watsoil_end, -SRF_mas_watsoil_beg] ) 1179 dSRF_mas_snow = Sprec ( [SRF_mas_snow_end , -SRF_mas_snow_beg] ) 1180 dSRF_mas_lake = Sprec ( [SRF_mas_lake_end , -SRF_mas_lake_beg] ) 1181 1182 echo ( '------------------------------------------------------------------------------------' ) 1183 echo ( f'\nSurface reservoirs -- {Title} ') 1184 echo ( f'SRF_mas_watveg_beg = {SRF_mas_watveg_beg :12.6e} kg | SRF_mas_watveg_end = {SRF_mas_watveg_end :12.6e} kg ' ) 1185 echo ( f'SRF_mas_watsoil_beg = {SRF_mas_watsoil_beg:12.6e} kg | SRF_mas_watsoil_end = {SRF_mas_watsoil_end:12.6e} kg ' ) 1186 echo ( f'SRF_mas_snow_beg = {SRF_mas_snow_beg :12.6e} kg | SRF_mas_snow_end = {SRF_mas_snow_end :12.6e} kg ' ) 1187 echo ( f'SRF_mas_lake_beg = {SRF_mas_lake_beg :12.6e} kg | SRF_mas_lake_end = {SRF_mas_lake_end :12.6e} kg ' ) 1188 1189 prtFlux ( 'dMass (watveg) ', dSRF_mas_watveg , 'e' , True ) 1190 prtFlux ( 'dMass (watsoil)', dSRF_mas_watsoil, 'e' , True ) 1191 prtFlux ( 'dMass (snow) ', dSRF_mas_snow , 'e' , True ) 1192 prtFlux ( 'dMass (lake) ', dSRF_mas_lake , 'e' , True ) 1193 1194 SRF_mas_wat_beg = Sprec ( [SRF_mas_watveg_beg , SRF_mas_watsoil_beg, SRF_mas_snow_beg] ) 1195 SRF_mas_wat_end = Sprec ( [SRF_mas_watveg_end , SRF_mas_watsoil_end, SRF_mas_snow_end] ) 1196 1197 dSRF_mas_wat = Sprec ( [+SRF_mas_watveg_end , +SRF_mas_watsoil_end, +SRF_mas_snow_end, 1198 -SRF_mas_watveg_beg , -SRF_mas_watsoil_beg, -SRF_mas_snow_beg] ) 1199 1200 echo ( '------------------------------------------------------------------------------------' ) 1201 echo ( f'Water content in surface -- {Title} ' ) 1202 echo ( f'SRF_mas_wat_beg = {SRF_mas_wat_beg:12.6e} kg | SRF_mas_wat_end = {SRF_mas_wat_end:12.6e} kg ') 1203 prtFlux ( 'dMass (water srf)', dSRF_mas_wat, 'e', True ) 1204 1205 echo ( '------------------------------------------------------------------------------------' ) 1206 echo ( 'Water content in ATM + SRF + RUN + LAKE' ) 1207 echo ( 'mas_wat_beg = {:12.6e} kg | mas_wat_end = {:12.6e} kg '. 1208 format (DYN_mas_wat_beg + ATM_mas_sno_beg + RUN_mas_wat_beg + SRF_mas_wat_beg + SRF_mas_lake_beg , 1209 DYN_mas_wat_end + ATM_mas_sno_end + RUN_mas_wat_end + SRF_mas_wat_end + SRF_mas_lake_end ) ) 1210 prtFlux ( 'dMass (water atm+srf+run+lake)', dDYN_mas_wat + dATM_mas_sno + dRUN_mas_wat + dSRF_mas_wat + dSRF_mas_lake, 'e', True) 1182 1211 1183 1212 echo ( '\n====================================================================================' ) … … 1186 1215 if ATM_HIS == 'latlon' : 1187 1216 echo ( ' latlon case' ) 1188 ATM_wbilo_oce = lmdz.geo2point ( rprec (d_ATM_his ['wbilo_oce']), dim1 D='cell' )1189 ATM_wbilo_sic = lmdz.geo2point ( rprec (d_ATM_his ['wbilo_sic']), dim1 D='cell' )1190 ATM_wbilo_ter = lmdz.geo2point ( rprec (d_ATM_his ['wbilo_ter']), dim1 D='cell' )1191 ATM_wbilo_lic = lmdz.geo2point ( rprec (d_ATM_his ['wbilo_lic']), dim1 D='cell' )1192 ATM_runofflic = lmdz.geo2point ( rprec (d_ATM_his ['runofflic']), dim1 D='cell' )1193 ATM_fqcalving = lmdz.geo2point ( rprec (d_ATM_his ['fqcalving']), dim1 D='cell' )1194 ATM_fqfonte = lmdz.geo2point ( rprec (d_ATM_his ['fqfonte'] ), dim1 D='cell' )1195 ATM_precip = lmdz.geo2point ( rprec (d_ATM_his ['precip'] ), dim1 D='cell' )1196 ATM_snowf = lmdz.geo2point ( rprec (d_ATM_his ['snow'] ), dim1 D='cell' )1197 ATM_evap = lmdz.geo2point ( rprec (d_ATM_his ['evap'] ), dim1 D='cell' )1198 ATM_wevap_ter = lmdz.geo2point ( rprec (d_ATM_his ['wevap_ter']), dim1 D='cell' )1199 ATM_wevap_oce = lmdz.geo2point ( rprec (d_ATM_his ['wevap_oce']), dim1 D='cell' )1200 ATM_wevap_lic = lmdz.geo2point ( rprec (d_ATM_his ['wevap_lic']), dim1 D='cell' )1201 ATM_wevap_sic = lmdz.geo2point ( rprec (d_ATM_his ['wevap_sic']), dim1 D='cell' )1202 ATM_wrain_ter = lmdz.geo2point ( rprec (d_ATM_his ['wrain_ter']), dim1 D='cell' )1203 ATM_wrain_oce = lmdz.geo2point ( rprec (d_ATM_his ['wrain_oce']), dim1 D='cell' )1204 ATM_wrain_lic = lmdz.geo2point ( rprec (d_ATM_his ['wrain_lic']), dim1 D='cell' )1205 ATM_wrain_sic = lmdz.geo2point ( rprec (d_ATM_his ['wrain_sic']), dim1 D='cell' )1206 ATM_wsnow_ter = lmdz.geo2point ( rprec (d_ATM_his ['wsnow_ter']), dim1 D='cell' )1207 ATM_wsnow_oce = lmdz.geo2point ( rprec (d_ATM_his ['wsnow_oce']), dim1 D='cell' )1208 ATM_wsnow_lic = lmdz.geo2point ( rprec (d_ATM_his ['wsnow_lic']), dim1 D='cell' )1209 ATM_wsnow_sic = lmdz.geo2point ( rprec (d_ATM_his ['wsnow_sic']), dim1 D='cell' )1210 ATM_runofflic = lmdz.geo2point ( rprec (d_ATM_his ['runofflic']), dim1 D='cell' )1211 echo ( f'End of LATLON case')1212 1217 ATM_wbilo_oce = lmdz.geo2point ( rprec (d_ATM_his ['wbilo_oce']), dim1d='cell' ) 1218 ATM_wbilo_sic = lmdz.geo2point ( rprec (d_ATM_his ['wbilo_sic']), dim1d='cell' ) 1219 ATM_wbilo_ter = lmdz.geo2point ( rprec (d_ATM_his ['wbilo_ter']), dim1d='cell' ) 1220 ATM_wbilo_lic = lmdz.geo2point ( rprec (d_ATM_his ['wbilo_lic']), dim1d='cell' ) 1221 ATM_runofflic = lmdz.geo2point ( rprec (d_ATM_his ['runofflic']), dim1d='cell' ) 1222 ATM_fqcalving = lmdz.geo2point ( rprec (d_ATM_his ['fqcalving']), dim1d='cell' ) 1223 ATM_fqfonte = lmdz.geo2point ( rprec (d_ATM_his ['fqfonte'] ), dim1d='cell' ) 1224 ATM_precip = lmdz.geo2point ( rprec (d_ATM_his ['precip'] ), dim1d='cell' ) 1225 ATM_snowf = lmdz.geo2point ( rprec (d_ATM_his ['snow'] ), dim1d='cell' ) 1226 ATM_evap = lmdz.geo2point ( rprec (d_ATM_his ['evap'] ), dim1d='cell' ) 1227 ATM_wevap_ter = lmdz.geo2point ( rprec (d_ATM_his ['wevap_ter']), dim1d='cell' ) 1228 ATM_wevap_oce = lmdz.geo2point ( rprec (d_ATM_his ['wevap_oce']), dim1d='cell' ) 1229 ATM_wevap_lic = lmdz.geo2point ( rprec (d_ATM_his ['wevap_lic']), dim1d='cell' ) 1230 ATM_wevap_sic = lmdz.geo2point ( rprec (d_ATM_his ['wevap_sic']), dim1d='cell' ) 1231 ATM_wrain_ter = lmdz.geo2point ( rprec (d_ATM_his ['wrain_ter']), dim1d='cell' ) 1232 ATM_wrain_oce = lmdz.geo2point ( rprec (d_ATM_his ['wrain_oce']), dim1d='cell' ) 1233 ATM_wrain_lic = lmdz.geo2point ( rprec (d_ATM_his ['wrain_lic']), dim1d='cell' ) 1234 ATM_wrain_sic = lmdz.geo2point ( rprec (d_ATM_his ['wrain_sic']), dim1d='cell' ) 1235 ATM_wsnow_ter = lmdz.geo2point ( rprec (d_ATM_his ['wsnow_ter']), dim1d='cell' ) 1236 ATM_wsnow_oce = lmdz.geo2point ( rprec (d_ATM_his ['wsnow_oce']), dim1d='cell' ) 1237 ATM_wsnow_lic = lmdz.geo2point ( rprec (d_ATM_his ['wsnow_lic']), dim1d='cell' ) 1238 ATM_wsnow_sic = lmdz.geo2point ( rprec (d_ATM_his ['wsnow_sic']), dim1d='cell' ) 1239 ATM_runofflic = lmdz.geo2point ( rprec (d_ATM_his ['runofflic']), dim1d='cell' ) 1240 echo ( 'End of LATLON case') 1241 1213 1242 if ATM_HIS == 'ico' : 1214 1243 echo (' ico case') … … 1240 1269 ATM_wsnow_lic = rprec (d_ATM_his ['wsnow_lic']) 1241 1270 ATM_wsnow_sic = rprec (d_ATM_his ['wsnow_sic']) 1242 echo ( f'End of ico case ')1271 echo ( 'End of ico case ') 1243 1272 1244 1273 echo ( 'ATM wprecip_oce' ) … … 1268 1297 ATM_wemp_sea = ATM_wevap_sic - ATM_wprecip_oce 1269 1298 1270 if RUN_HIS == 'latlon' : 1271 echo ( f'RUN costalflow Grille LATLON' ) 1272 if TestInterp : 1273 echo ( f'RUN runoff TestInterp' ) 1274 RUN_runoff = lmdz.geo2point ( rprec (d_RUN_his ['runoff_contfrac_interp'] ) , dim1D='cell' ) 1275 RUN_drainage = lmdz.geo2point ( rprec (d_RUN_his ['drainage_contfrac_interp']) , dim1D='cell' ) 1276 else : 1277 echo ( f'RUN runoff' ) 1278 RUN_runoff = lmdz.geo2point ( rprec (d_RUN_his ['runoff'] ), dim1D='cell' ) 1279 RUN_drainage = lmdz.geo2point ( rprec (d_RUN_his ['drainage'] ), dim1D='cell' ) 1280 1281 RUN_coastalflow = lmdz.geo2point ( rprec (d_RUN_his ['coastalflow'] ), dim1D='cell' ) 1282 RUN_riverflow = lmdz.geo2point ( rprec (d_RUN_his ['riverflow'] ), dim1D='cell' ) 1283 RUN_riversret = lmdz.geo2point ( rprec (d_RUN_his ['riversret'] ), dim1D='cell' ) 1284 RUN_coastalflow_cpl = lmdz.geo2point ( rprec (d_RUN_his ['coastalflow_cpl']), dim1D='cell' ) 1285 RUN_riverflow_cpl = lmdz.geo2point ( rprec (d_RUN_his ['riverflow_cpl'] ), dim1D='cell' ) 1286 1287 if RUN_HIS == 'ico' : 1288 echo ( f'RUN costalflow Grille ICO' ) 1289 RUN_coastalflow = rprec (d_RUN_his ['coastalflow']) 1290 RUN_riverflow = rprec (d_RUN_his ['riverflow'] ) 1291 RUN_runoff = rprec (d_RUN_his ['runoff'] ) 1292 RUN_drainage = rprec (d_RUN_his ['drainage'] ) 1293 RUN_riversret = rprec (d_RUN_his ['riversret'] ) 1294 1295 RUN_coastalflow_cpl = rprec (d_RUN_his ['coastalflow_cpl']) 1296 RUN_riverflow_cpl = rprec (d_RUN_his ['riverflow_cpl'] ) 1297 1298 Step = 0 1299 1300 if SRF_HIS == 'latlon' : 1301 if TestInterp : 1302 echo ( f'SRF rain TestInterp' ) 1303 SRF_rain = lmdz.geo2point ( rprec (d_SRF_his ['rain_contfrac_interp'] ), dim1D='cell') 1304 SRF_evap = lmdz.geo2point ( rprec (d_SRF_his ['evap_contfrac_interp'] ), dim1D='cell') 1305 SRF_snowf = lmdz.geo2point ( rprec (d_SRF_his ['snow_contfrac_interp'] ), dim1D='cell') 1306 SRF_subli = lmdz.geo2point ( rprec (d_SRF_his ['subli_contfrac_interp']), dim1D='cell') 1307 SRF_transpir = lmdz.geo2point ( rprec (d_SRF_his ['transpir_contfrac_interp']).sum(dim='veget'), dim1D='cell' ) 1308 #SRF_rain.attrs.update ( d_SRF_his ['rain_contfrac_interp'].attrs ) 1309 #SRF_evap.attrs.update ( d_SRF_his ['evap_contfrac_interp'].attrs ) 1310 #SRF_snowf.attrs.update ( d_SRF_his ['snow_contfrac_interp'].attrs ) 1311 #SRF_subli.attrs.update ( d_SRF_his ['subli_contfrac_interp'].attrs ) 1312 #SRF_transpir.attrs.update ( d_SRF_his ['transpir_contfrac_interp'].attrs ) 1313 else : 1314 echo ( f'SRF rain' ) 1315 SRF_rain = lmdz.geo2point ( rprec (d_SRF_his ['rain'] ) , dim1D='cell') 1316 SRF_evap = lmdz.geo2point ( rprec (d_SRF_his ['evap'] ) , dim1D='cell') 1317 SRF_snowf = lmdz.geo2point ( rprec (d_SRF_his ['snowf']) , dim1D='cell') 1318 SRF_subli = lmdz.geo2point ( rprec (d_SRF_his ['subli']) , dim1D='cell') 1319 SRF_transpir = lmdz.geo2point ( rprec (d_SRF_his ['transpir']).sum(dim='veget'), dim1D='cell' ) 1320 1321 if SRF_HIS == 'ico' : 1322 echo ( f'SRF rain') 1323 SRF_rain = rprec (d_SRF_his ['rain'] ) 1324 SRF_evap = rprec (d_SRF_his ['evap'] ) 1325 SRF_snowf = rprec (d_SRF_his ['snowf']) 1326 SRF_subli = rprec (d_SRF_his ['subli']) 1327 SRF_transpir = rprec (d_SRF_his ['transpir']).sum(dim='veget') 1328 1329 echo ( f'SRF emp' ) 1330 SRF_transpir.attrs['units'] = d_SRF_his ['transpir'].attrs['units'] 1331 SRF_emp = SRF_evap - SRF_rain - SRF_snowf ; SRF_emp.attrs['units'] = SRF_rain.attrs['units'] 1332 1333 ## Correcting units of SECHIBA variables 1334 def mmd2SI ( Var ) : 1335 '''Change unit from mm/d or m^3/s to kg/s if needed''' 1336 if 'units' in VarT.attrs : 1337 if VarT.attrs['units'] in ['m^3/s', 'm3/s', 'm3.s-1',] : 1338 VarT.values = VarT.values * ATM_rho ; VarT.attrs['units'] = 'kg/s' 1339 if VarT.attrs['units'] == 'mm/d' : 1340 VarT.values = VarT.values * ATM_rho * (1e-3/86400.) ; VarT.attrs['units'] = 'kg/s' 1341 if VarT.attrs['units'] in ['m^3', 'm3', ] : 1342 VarT.values = VarT.values * ATM_rho ; VarT.attrs['units'] = 'kg' 1343 1344 for var in [ 'runoff', 'drainage', 'riversret', 'coastalflow', 'riverflow', 'coastalflow_cpl', 'riverflow_cpl' ] : 1345 VarT = locals()['RUN_' + var] 1346 mmd2SI (VarT) 1347 1348 for var in ['evap', 'snowf', 'subli', 'transpir', 'rain', 'emp' ] : 1349 VarT = locals()['SRF_' + var] 1350 mmd2SI (VarT) 1351 1352 echo ( f'RUN input' ) 1353 RUN_input = RUN_runoff + RUN_drainage 1354 RUN_output = RUN_coastalflow + RUN_riverflow 1355 1356 echo ( f'ATM flw_wbilo' ) 1299 if SRF : 1300 if RUN_HIS == 'latlon' : 1301 echo ( 'RUN costalflow Grille LATLON' ) 1302 if TestInterp : 1303 echo ( 'RUN runoff TestInterp' ) 1304 RUN_runoff = lmdz.geo2point ( rprec (d_RUN_his ['runoff_contfrac_interp'] ) , dim1d='cell' ) 1305 RUN_drainage = lmdz.geo2point ( rprec (d_RUN_his ['drainage_contfrac_interp']) , dim1d='cell' ) 1306 else : 1307 echo ( 'RUN runoff' ) 1308 RUN_runoff = lmdz.geo2point ( rprec (d_RUN_his ['runoff'] ), dim1d='cell' ) 1309 RUN_drainage = lmdz.geo2point ( rprec (d_RUN_his ['drainage'] ), dim1d='cell' ) 1310 1311 RUN_coastalflow = lmdz.geo2point ( rprec (d_RUN_his ['coastalflow'] ), dim1d='cell' ) 1312 RUN_riverflow = lmdz.geo2point ( rprec (d_RUN_his ['riverflow'] ), dim1d='cell' ) 1313 RUN_riversret = lmdz.geo2point ( rprec (d_RUN_his ['riversret'] ), dim1d='cell' ) 1314 RUN_coastalflow_cpl = lmdz.geo2point ( rprec (d_RUN_his ['coastalflow_cpl']), dim1d='cell' ) 1315 RUN_riverflow_cpl = lmdz.geo2point ( rprec (d_RUN_his ['riverflow_cpl'] ), dim1d='cell' ) 1316 1317 if RUN_HIS == 'ico' : 1318 echo ( 'RUN costalflow Grille ICO' ) 1319 RUN_coastalflow = rprec (d_RUN_his ['coastalflow']) 1320 RUN_riverflow = rprec (d_RUN_his ['riverflow'] ) 1321 RUN_runoff = rprec (d_RUN_his ['runoff'] ) 1322 RUN_drainage = rprec (d_RUN_his ['drainage'] ) 1323 RUN_riversret = rprec (d_RUN_his ['riversret'] ) 1324 1325 RUN_coastalflow_cpl = rprec (d_RUN_his ['coastalflow_cpl']) 1326 RUN_riverflow_cpl = rprec (d_RUN_his ['riverflow_cpl'] ) 1327 1328 Step = 0 1329 1330 if SRF_HIS == 'latlon' : 1331 if TestInterp : 1332 echo ( 'SRF rain TestInterp' ) 1333 SRF_rain = lmdz.geo2point ( rprec (d_SRF_his ['rain_contfrac_interp'] ), dim1d='cell') 1334 SRF_evap = lmdz.geo2point ( rprec (d_SRF_his ['evap_contfrac_interp'] ), dim1d='cell') 1335 SRF_snowf = lmdz.geo2point ( rprec (d_SRF_his ['snow_contfrac_interp'] ), dim1d='cell') 1336 SRF_subli = lmdz.geo2point ( rprec (d_SRF_his ['subli_contfrac_interp']), dim1d='cell') 1337 SRF_transpir = lmdz.geo2point ( rprec (d_SRF_his ['transpir_contfrac_interp']).sum(dim='veget'), dim1d='cell' ) 1338 #SRF_rain.attrs.update ( d_SRF_his ['rain_contfrac_interp'].attrs ) 1339 #SRF_evap.attrs.update ( d_SRF_his ['evap_contfrac_interp'].attrs ) 1340 #SRF_snowf.attrs.update ( d_SRF_his ['snow_contfrac_interp'].attrs ) 1341 #SRF_subli.attrs.update ( d_SRF_his ['subli_contfrac_interp'].attrs ) 1342 #SRF_transpir.attrs.update ( d_SRF_his ['transpir_contfrac_interp'].attrs ) 1343 else : 1344 echo ( 'SRF rain' ) 1345 SRF_rain = lmdz.geo2point ( rprec (d_SRF_his ['rain'] ) , dim1d='cell') 1346 SRF_evap = lmdz.geo2point ( rprec (d_SRF_his ['evap'] ) , dim1d='cell') 1347 SRF_snowf = lmdz.geo2point ( rprec (d_SRF_his ['snowf']) , dim1d='cell') 1348 SRF_subli = lmdz.geo2point ( rprec (d_SRF_his ['subli']) , dim1d='cell') 1349 SRF_transpir = lmdz.geo2point ( rprec (d_SRF_his ['transpir']).sum(dim='veget'), dim1d='cell' ) 1350 1351 if SRF_HIS == 'ico' : 1352 echo ( 'SRF rain') 1353 SRF_rain = rprec (d_SRF_his ['rain'] ) 1354 SRF_evap = rprec (d_SRF_his ['evap'] ) 1355 SRF_snowf = rprec (d_SRF_his ['snowf']) 1356 SRF_subli = rprec (d_SRF_his ['subli']) 1357 SRF_transpir = rprec (d_SRF_his ['transpir']).sum(dim='veget') 1358 1359 echo ( 'SRF emp' ) 1360 SRF_transpir.attrs['units'] = d_SRF_his ['transpir'].attrs['units'] 1361 SRF_emp = SRF_evap - SRF_rain - SRF_snowf ; SRF_emp.attrs['units'] = SRF_rain.attrs['units'] 1362 1363 ## Correcting units of SECHIBA variables 1364 def mmd2si ( pvar ) : 1365 '''Change unit from mm/d or m^3/s to kg/s if needed''' 1366 if 'units' in pvar.attrs : 1367 if pvar.attrs['units'] in ['m^3/s', 'm3/s', 'm3.s-1',] : 1368 pvar.values = pvar.values * ATM_RHO ; pvar.attrs['units'] = 'kg/s' 1369 if pvar.attrs['units'] == 'mm/d' : 1370 pvar.values = pvar.values * ATM_RHO * (1e-3/lmdz.RDAY) ; pvar.attrs['units'] = 'kg/s' 1371 if pvar.attrs['units'] in ['m^3', 'm3', ] : 1372 pvar.values = pvar.values * ATM_RHO ; pvar.attrs['units'] = 'kg' 1373 1374 for var in [ 'runoff', 'drainage', 'riversret', 'coastalflow', 'riverflow', 'coastalflow_cpl', 'riverflow_cpl' ] : 1375 zvar = locals()['RUN_' + var] 1376 mmd2si (zvar) 1377 1378 for var in ['evap', 'snowf', 'subli', 'transpir', 'rain', 'emp' ] : 1379 zvar = locals()['SRF_' + var] 1380 mmd2si (zvar) 1381 1382 echo ( 'RUN input' ) 1383 RUN_input = RUN_runoff + RUN_drainage 1384 RUN_output = RUN_coastalflow + RUN_riverflow 1385 1386 echo ( 'ATM flw_wbilo' ) 1357 1387 ATM_flx_wbilo = ATM_flux_int ( ATM_wbilo ) 1358 1388 ATM_flx_wevap = ATM_flux_int ( ATM_wevap ) … … 1367 1397 ATM_flx_wbilo_sic = ATM_flux_int ( ATM_wbilo_sic ) 1368 1398 ATM_flx_wbilo_ter = ATM_flux_int ( ATM_wbilo_ter ) 1399 # Type d'integration a verifier 1369 1400 ATM_flx_calving = ATM_flux_int ( ATM_fqcalving ) 1370 1401 ATM_flx_fqfonte = ATM_flux_int ( ATM_fqfonte ) … … 1373 1404 LIC_flx_fqfonte = LIC_flux_int ( ATM_fqfonte ) 1374 1405 1375 echo ( f'ATM flx precip' )1406 echo ( 'ATM flx precip' ) 1376 1407 ATM_flx_precip = ATM_flux_int ( ATM_precip ) 1377 1408 ATM_flx_snowf = ATM_flux_int ( ATM_snowf ) … … 1384 1415 LIC_flx_runlic = LIC_flux_int ( ATM_runofflic ) 1385 1416 1386 echo ( f'ATM flx_wrain_ter' )1417 echo ( 'ATM flx_wrain_ter' ) 1387 1418 ATM_flx_wrain_ter = ATM_flux_int ( ATM_wrain_ter ) 1388 1419 ATM_flx_wrain_oce = ATM_flux_int ( ATM_wrain_oce ) … … 1397 1428 ATM_flx_wsnow_sea = ATM_flux_int ( ATM_wsnow_sea ) 1398 1429 1399 echo ( f'ATM flx_evap_ter' )1430 echo ( 'ATM flx_evap_ter' ) 1400 1431 ATM_flx_wevap_ter = ATM_flux_int ( ATM_wevap_ter ) 1401 1432 ATM_flx_wevap_oce = ATM_flux_int ( ATM_wevap_oce ) … … 1416 1447 ATM_flx_emp = ATM_flux_int ( ATM_emp ) 1417 1448 1418 echo ( f'RUN flx_coastal' ) 1419 RUN_flx_coastal = ONE_flux_int ( RUN_coastalflow) 1420 echo ( f'RUN flx_river' ) 1421 RUN_flx_river = ONE_flux_int ( RUN_riverflow ) 1422 echo ( f'RUN flx_coastal_cpl' ) 1423 RUN_flx_coastal_cpl = ONE_flux_int ( RUN_coastalflow_cpl) 1424 echo ( f'RUN flx_river_cpl' ) 1425 RUN_flx_river_cpl = ONE_flux_int ( RUN_riverflow_cpl ) 1426 echo ( f'RUN flx_drainage' ) 1427 RUN_flx_drainage = SRF_flux_int ( RUN_drainage ) 1428 echo ( f'RUN flx_riversset' ) 1429 RUN_flx_riversret = SRF_flux_int ( RUN_riversret ) 1430 echo ( f'RUN flx_runoff' ) 1431 RUN_flx_runoff = SRF_flux_int ( RUN_runoff ) 1432 echo ( f'RUN flx_input' ) 1433 RUN_flx_input = SRF_flux_int ( RUN_input ) 1434 echo ( f'RUN flx_output' ) 1435 RUN_flx_output = ONE_flux_int ( RUN_output ) 1436 1437 echo ( f'RUN flx_bil' ) ; Step += 1 1438 #RUN_flx_bil = RUN_flx_input - RUN_flx_output 1439 #RUN_flx_rivcoa = RUN_flx_coastal + RUN_flx_river 1440 1441 RUN_flx_bil = ONE_flux_int ( RUN_input - RUN_output) 1442 RUN_flx_rivcoa = ONE_flux_int ( RUN_coastalflow + RUN_riverflow) 1443 1444 prtFlux ('wbilo_oce ', ATM_flx_wbilo_oce , 'f' ) 1445 prtFlux ('wbilo_sic ', ATM_flx_wbilo_sic , 'f' ) 1446 prtFlux ('wbilo_sic+oce ', ATM_flx_wbilo_sea , 'f' ) 1447 prtFlux ('wbilo_ter ', ATM_flx_wbilo_ter , 'f' ) 1448 prtFlux ('wbilo_lic ', ATM_flx_wbilo_lic , 'f' ) 1449 prtFlux ('Sum wbilo_* ', ATM_flx_wbilo , 'f', True) 1450 prtFlux ('E-P ', ATM_flx_emp , 'f', True) 1451 prtFlux ('calving ', ATM_flx_calving , 'f' ) 1452 prtFlux ('fqfonte ', ATM_flx_fqfonte , 'f' ) 1453 prtFlux ('precip ', ATM_flx_precip , 'f' ) 1454 prtFlux ('snowf ', ATM_flx_snowf , 'f' ) 1449 if SRF : 1450 echo ( 'RUN flx_coastal' ) 1451 RUN_flx_coastal = ONE_flux_int ( RUN_coastalflow) 1452 echo ( 'RUN flx_river' ) 1453 RUN_flx_river = ONE_flux_int ( RUN_riverflow ) 1454 echo ( 'RUN flx_coastal_cpl' ) 1455 RUN_flx_coastal_cpl = ONE_flux_int ( RUN_coastalflow_cpl) 1456 echo ( 'RUN flx_river_cpl' ) 1457 RUN_flx_river_cpl = ONE_flux_int ( RUN_riverflow_cpl ) 1458 echo ( 'RUN flx_drainage' ) 1459 RUN_flx_drainage = SRF_flux_int ( RUN_drainage ) 1460 echo ( 'RUN flx_riversset' ) 1461 RUN_flx_riversret = SRF_flux_int ( RUN_riversret ) 1462 echo ( 'RUN flx_runoff' ) 1463 RUN_flx_runoff = SRF_flux_int ( RUN_runoff ) 1464 echo ( 'RUN flx_input' ) 1465 RUN_flx_input = SRF_flux_int ( RUN_input ) 1466 echo ( 'RUN flx_output' ) 1467 RUN_flx_output = ONE_flux_int ( RUN_output ) 1468 1469 echo ( 'RUN flx_bil' ) ; Step += 1 1470 #RUN_flx_bil = RUN_flx_input - RUN_flx_output 1471 #RUN_flx_rivcoa = RUN_flx_coastal + RUN_flx_river 1472 1473 RUN_flx_bil = ONE_flux_int ( RUN_input - RUN_output) 1474 RUN_flx_rivcoa = ONE_flux_int ( RUN_coastalflow + RUN_riverflow) 1475 1476 prtFlux ('wbilo_oce ', ATM_flx_wbilo_oce , 'f' ) 1477 prtFlux ('wbilo_sic ', ATM_flx_wbilo_sic , 'f' ) 1478 prtFlux ('wbilo_sic+oce ', ATM_flx_wbilo_sea , 'f' ) 1479 prtFlux ('wbilo_ter ', ATM_flx_wbilo_ter , 'f' ) 1480 prtFlux ('wbilo_lic ', ATM_flx_wbilo_lic , 'f' ) 1481 prtFlux ('Sum wbilo_* ', ATM_flx_wbilo , 'f', True) 1482 prtFlux ('E-P ', ATM_flx_emp , 'f', True) 1483 prtFlux ('calving ', ATM_flx_calving , 'f' ) 1484 prtFlux ('fqfonte ', ATM_flx_fqfonte , 'f' ) 1485 prtFlux ('precip ', ATM_flx_precip , 'f' ) 1486 prtFlux ('snowf ', ATM_flx_snowf , 'f' ) 1455 1487 prtFlux ('evap ', ATM_flx_evap , 'f' ) 1456 1488 prtFlux ('runoff lic ', ATM_flx_runlic , 'f' ) … … 1467 1499 prtFlux ('ERROR emp ', ATM_flx_wemp - ATM_flx_emp , 'e', True ) 1468 1500 1469 1470 echo ( '\n====================================================================================' )1471 echo ( f'-- RUNOFF Fluxes -- {Title} ' )1472 prtFlux ('coastalflow ', RUN_flx_coastal , 'f' ) 1473 prtFlux ('riverflow ', RUN_flx_river , 'f' ) 1474 prtFlux ('coastal_cpl ', RUN_flx_coastal_cpl, 'f' ) 1475 prtFlux ('riverf_cpl ', RUN_flx_river_cpl , 'f' ) 1476 prtFlux ('river+coastal ', RUN_flx_rivcoa , 'f' ) 1477 prtFlux ('drainage ', RUN_flx_drainage , 'f' ) 1478 prtFlux ('riversret ', RUN_flx_riversret , 'f' ) 1479 prtFlux ('runoff ', RUN_flx_runoff , 'f' ) 1480 prtFlux ('river in ', RUN_flx_input , 'f' ) 1481 prtFlux ('river out ', RUN_flx_output , 'f' ) 1482 prtFlux ('river bil ', RUN_flx_bil , 'f' ) 1501 if SRF : 1502 echo ( '\n====================================================================================' ) 1503 echo ( f'-- RUNOFF Fluxes -- {Title} ' ) 1504 prtFlux ('coastalflow ', RUN_flx_coastal , 'f' ) 1505 prtFlux ('riverflow ', RUN_flx_river , 'f' ) 1506 prtFlux ('coastal_cpl ', RUN_flx_coastal_cpl, 'f' ) 1507 prtFlux ('riverf_cpl ', RUN_flx_river_cpl , 'f' ) 1508 prtFlux ('river+coastal ', RUN_flx_rivcoa , 'f' ) 1509 prtFlux ('drainage ', RUN_flx_drainage , 'f' ) 1510 prtFlux ('riversret ', RUN_flx_riversret , 'f' ) 1511 prtFlux ('runoff ', RUN_flx_runoff , 'f' ) 1512 prtFlux ('river in ', RUN_flx_input , 'f' ) 1513 prtFlux ('river out ', RUN_flx_output , 'f' ) 1514 prtFlux ('river bil ', RUN_flx_bil , 'f' ) 1483 1515 1484 1516 ATM_flx_budget = -ATM_flx_wbilo + ATM_flx_calving + ATM_flx_runlic #+ ATM_flx_fqfonte + RUN_flx_river … … 1542 1574 echo ( 'LIC error (-wbilo_lic - runofflic*frac_lic) = {:12.4e} (rel) '.format ( (LIC_flx_budget3-dLIC_mas_wat)/dLIC_mas_wat) ) 1543 1575 1544 echo ( '\n====================================================================================' ) 1545 echo ( f'-- SECHIBA fluxes -- {Title} ' ) 1546 1547 SRF_flx_rain = SRF_flux_int ( SRF_rain ) 1548 SRF_flx_evap = SRF_flux_int ( SRF_evap ) 1549 SRF_flx_snowf = SRF_flux_int ( SRF_snowf ) 1550 SRF_flx_subli = SRF_flux_int ( SRF_subli ) 1551 SRF_flx_transpir = SRF_flux_int ( SRF_transpir ) 1552 SRF_flx_emp = SRF_flux_int ( SRF_emp ) 1553 1554 RUN_flx_torouting = SRF_flux_int ( RUN_runoff + RUN_drainage) 1555 RUN_flx_fromrouting = ONE_flux_int ( RUN_coastalflow + RUN_riverflow ) 1556 1557 SRF_flx_all = SRF_flux_int ( SRF_rain + SRF_snowf - SRF_evap - RUN_runoff - RUN_drainage ) 1558 1559 prtFlux ('rain ', SRF_flx_rain , 'f' ) 1560 prtFlux ('evap ', SRF_flx_evap , 'f' ) 1561 prtFlux ('snowf ', SRF_flx_snowf , 'f' ) 1562 prtFlux ('E-P ', SRF_flx_emp , 'f' ) 1563 prtFlux ('subli ', SRF_flx_subli , 'f' ) 1564 prtFlux ('transpir ', SRF_flx_transpir , 'f' ) 1565 prtFlux ('to routing ', RUN_flx_torouting , 'f' ) 1566 prtFlux ('budget ', SRF_flx_all , 'f', small=True ) 1567 1568 echo ( '\n------------------------------------------------------------------------------------' ) 1569 echo ( 'Water content in surface ' ) 1570 echo ( f'SRF_mas_wat_beg = {SRF_mas_wat_beg:12.6e} kg | SRF_mas_wat_end = {SRF_mas_wat_end:12.6e} kg ' ) 1571 prtFlux ( 'dMass (water srf)', dSRF_mas_wat, 'e', small=True) 1572 prtFlux ( 'Error ', SRF_flx_all-dSRF_mas_wat, 'e', small=True ) 1573 echo ( 'dMass (water srf) = {:12.4e} (rel) '.format ( (SRF_flx_all-dSRF_mas_wat)/dSRF_mas_wat) ) 1574 1575 echo ( '\n====================================================================================' ) 1576 echo ( f'-- Check ATM vs. SRF -- {Title} ' ) 1577 prtFlux ('E-P ATM ', ATM_flx_wemp_ter , 'f' ) 1578 prtFlux ('wbilo ter ', ATM_flx_wbilo_ter , 'f' ) 1579 prtFlux ('E-P SRF ', SRF_flx_emp , 'f' ) 1580 prtFlux ('SRF/ATM error ', ATM_flx_wbilo_ter - SRF_flx_emp, 'e', True) 1581 echo ( 'SRF/ATM error {:12.3e} (rel) '.format ( (ATM_flx_wbilo_ter - SRF_flx_emp)/SRF_flx_emp ) ) 1582 1583 echo ('') 1584 echo ( '\n====================================================================================' ) 1585 echo ( f'-- RUNOFF fluxes -- {Title} ' ) 1586 RUN_flx_all = RUN_flx_torouting - RUN_flx_river - RUN_flx_coastal 1587 prtFlux ('runoff ', RUN_flx_runoff , 'f' ) 1588 prtFlux ('drainage ', RUN_flx_drainage , 'f' ) 1589 prtFlux ('run+drain ', RUN_flx_torouting , 'f' ) 1590 prtFlux ('river ', RUN_flx_river , 'f' ) 1591 prtFlux ('coastal ', RUN_flx_coastal , 'f' ) 1592 prtFlux ('riv+coa ', RUN_flx_fromrouting , 'f' ) 1593 prtFlux ('budget ', RUN_flx_all , 'f' , small=True) 1594 1595 echo ( '\n------------------------------------------------------------------------------------' ) 1596 echo ( f'Water content in routing+lake -- {Title} ' ) 1597 echo ( f'RUN_mas_wat_beg = {RUN_mas_wat_beg:12.6e} kg | RUN_mas_wat_end = {RUN_mas_wat_end:12.6e} kg ' ) 1598 prtFlux ( 'dMass (routing) ', dRUN_mas_wat+dSRF_mas_lake, 'f', small=True) 1599 prtFlux ( 'Routing error ', RUN_flx_all+dSRF_mas_lake-dRUN_mas_wat, 'e', small=True ) 1600 echo ( 'Routing error : {:12.3e} (rel)'.format ( (RUN_flx_all-dSRF_mas_lake-dRUN_mas_wat)/(dRUN_mas_wat+dSRF_mas_lake) ) ) 1601 1602 echo ( '\n------------------------------------------------------------------------------------' ) 1603 echo ( f'Water content in routing -- {Title} ' ) 1604 echo ( f'RUN_mas_wat_beg = {RUN_mas_wat_beg:12.6e} kg | RUN_mas_wat_end = {RUN_mas_wat_end:12.6e} kg ' ) 1605 prtFlux ( 'dMass (routing) ', dRUN_mas_wat, 'f', small=True ) 1606 prtFlux ( 'Routing error ', RUN_flx_all-dRUN_mas_wat, 'e', small=True) 1607 echo ( 'Routing error : {:12.3e} (rel)'.format ( (RUN_flx_all-dRUN_mas_wat)/dRUN_mas_wat ) ) 1576 if SRF : 1577 echo ( '\n====================================================================================' ) 1578 echo ( f'-- SECHIBA fluxes -- {Title} ' ) 1579 1580 SRF_flx_rain = SRF_flux_int ( SRF_rain ) 1581 SRF_flx_evap = SRF_flux_int ( SRF_evap ) 1582 SRF_flx_snowf = SRF_flux_int ( SRF_snowf ) 1583 SRF_flx_subli = SRF_flux_int ( SRF_subli ) 1584 SRF_flx_transpir = SRF_flux_int ( SRF_transpir ) 1585 SRF_flx_emp = SRF_flux_int ( SRF_emp ) 1586 1587 RUN_flx_torouting = SRF_flux_int ( RUN_runoff + RUN_drainage) 1588 RUN_flx_fromrouting = ONE_flux_int ( RUN_coastalflow + RUN_riverflow ) 1589 1590 SRF_flx_all = SRF_flux_int ( SRF_rain + SRF_snowf - SRF_evap - RUN_runoff - RUN_drainage ) 1591 1592 prtFlux ('rain ', SRF_flx_rain , 'f' ) 1593 prtFlux ('evap ', SRF_flx_evap , 'f' ) 1594 prtFlux ('snowf ', SRF_flx_snowf , 'f' ) 1595 prtFlux ('E-P ', SRF_flx_emp , 'f' ) 1596 prtFlux ('subli ', SRF_flx_subli , 'f' ) 1597 prtFlux ('transpir ', SRF_flx_transpir , 'f' ) 1598 prtFlux ('to routing ', RUN_flx_torouting , 'f' ) 1599 prtFlux ('budget ', SRF_flx_all , 'f', small=True ) 1600 1601 echo ( '\n------------------------------------------------------------------------------------' ) 1602 echo ( 'Water content in surface ' ) 1603 echo ( f'SRF_mas_wat_beg = {SRF_mas_wat_beg:12.6e} kg | SRF_mas_wat_end = {SRF_mas_wat_end:12.6e} kg ' ) 1604 prtFlux ( 'dMass (water srf)', dSRF_mas_wat, 'e', small=True) 1605 prtFlux ( 'Error ', SRF_flx_all-dSRF_mas_wat, 'e', small=True ) 1606 echo ( 'dMass (water srf) = {:12.4e} (rel) '.format ( (SRF_flx_all-dSRF_mas_wat)/dSRF_mas_wat) ) 1607 1608 echo ( '\n====================================================================================' ) 1609 echo ( f'-- Check ATM vs. SRF -- {Title} ' ) 1610 prtFlux ('E-P ATM ', ATM_flx_wemp_ter , 'f' ) 1611 prtFlux ('wbilo ter ', ATM_flx_wbilo_ter , 'f' ) 1612 prtFlux ('E-P SRF ', SRF_flx_emp , 'f' ) 1613 prtFlux ('SRF/ATM error ', ATM_flx_wbilo_ter - SRF_flx_emp, 'e', True) 1614 echo ( 'SRF/ATM error {:12.3e} (rel) '.format ( (ATM_flx_wbilo_ter - SRF_flx_emp)/SRF_flx_emp ) ) 1615 1616 echo ('') 1617 echo ( '\n====================================================================================' ) 1618 echo ( f'-- RUNOFF fluxes -- {Title} ' ) 1619 RUN_flx_all = RUN_flx_torouting - RUN_flx_river - RUN_flx_coastal 1620 prtFlux ('runoff ', RUN_flx_runoff , 'f' ) 1621 prtFlux ('drainage ', RUN_flx_drainage , 'f' ) 1622 prtFlux ('run+drain ', RUN_flx_torouting , 'f' ) 1623 prtFlux ('river ', RUN_flx_river , 'f' ) 1624 prtFlux ('coastal ', RUN_flx_coastal , 'f' ) 1625 prtFlux ('riv+coa ', RUN_flx_fromrouting , 'f' ) 1626 prtFlux ('budget ', RUN_flx_all , 'f' , small=True) 1627 1628 echo ( '\n------------------------------------------------------------------------------------' ) 1629 echo ( f'Water content in routing+lake -- {Title} ' ) 1630 echo ( f'RUN_mas_wat_beg = {RUN_mas_wat_beg:12.6e} kg | RUN_mas_wat_end = {RUN_mas_wat_end:12.6e} kg ' ) 1631 prtFlux ( 'dMass (routing) ', dRUN_mas_wat+dSRF_mas_lake, 'f', small=True) 1632 prtFlux ( 'Routing error ', RUN_flx_all+dSRF_mas_lake-dRUN_mas_wat, 'e', small=True ) 1633 echo ( 'Routing error : {:12.3e} (rel)'.format ( (RUN_flx_all-dSRF_mas_lake-dRUN_mas_wat)/(dRUN_mas_wat+dSRF_mas_lake) ) ) 1634 1635 echo ( '\n------------------------------------------------------------------------------------' ) 1636 echo ( f'Water content in routing -- {Title} ' ) 1637 echo ( f'RUN_mas_wat_beg = {RUN_mas_wat_beg:12.6e} kg | RUN_mas_wat_end = {RUN_mas_wat_end:12.6e} kg ' ) 1638 prtFlux ( 'dMass (routing) ', dRUN_mas_wat, 'f', small=True ) 1639 prtFlux ( 'Routing error ', RUN_flx_all-dRUN_mas_wat, 'e', small=True) 1640 echo ( 'Routing error : {:12.3e} (rel)'.format ( (RUN_flx_all-dRUN_mas_wat)/dRUN_mas_wat ) ) 1608 1641 1609 1642 echo ( ' ' ) … … 1611 1644 1612 1645 echo ( 'SVN Information' ) 1613 for clef in SVN.keys () : echo ( SVN[clef] ) 1646 for kk in SVN.keys(): 1647 print ( SVN[kk] ) -
TOOLS/WATER_BUDGET/CPL_waterbudget.py
r6651 r6665 42 42 import numpy as np, xarray as xr 43 43 44 # Check python version45 if sys.version_info < (3, 8, 0) :46 print ( f'Python version : {platform.python_version()}' )47 raise Exception ( "Minimum Python version is 3.8" )48 49 44 ## Import local modules 50 45 import WaterUtils as wu … … 52 47 import nemo, lmdz 53 48 54 from WaterUtils import VarInt, Rho, Ra, Grav, ICE_rho_ice, ICE_rho_sno, OCE_rho_liq, ATM_rho, SRF_rho, RUN_rho, ICE_rho_pnd, YearLength 49 from WaterUtils import RA, GRAV, ICE_RHO_ICE, ICE_RHO_SNO, OCE_RHO_LIQ, \ 50 ATM_RHO, SRF_RHO, RUN_RHO, ICE_RHO_PND, YEAR_LENGTH 55 51 56 52 ## Creates parser for reading .ini input file … … 66 62 YearBegin=None ; YearEnd=None ; DateBegin=None ; DateEnd=None 67 63 64 Timer=False ; Debug=False 68 65 ## 69 66 ARCHIVE=None ; STORAGE=None ; SCRATCHDIR=None ; R_IN=None ; rebuild=None ; TmpDir=None 70 FileDir=None ; FileOut=None 67 FileDir=None ; FileOut=None ; R_OUT=None ; R_FIG=None ; R_FIGR=None ; R_BUFR=None ; R_SAVE=None 68 R_BUF_KSH=None ; REBUILD_DIR=None ; POST_DIR=None ; L_EXP=None 69 71 70 dir_ATM_his=None ; dir_SRF_his=None ; dir_OCE_his=None ; dir_ICE_his=None 72 71 FileCommon=None ; file_ATM_his=None ; file_SRF_his=None ; file_RUN_his=None … … 77 76 file_ICE_beg=None ; file_OCE_beg=None 78 77 file_OCE_end=None ; file_ICE_beg=None ; file_OCE_end=None ; file_ICE_end=None 78 TarRestartDate_beg=None ; TarRestartDate_end=None 79 file_DYN_aire=None 79 80 tar_restart_beg_ATM=None ; tar_restart_beg_DYN=None ; tar_restart_beg_SRF=None 80 81 tar_restart_beg_RUN=None ; tar_restart_beg_OCE=None ; tar_restart_beg_ICE=None … … 82 83 tar_restart_end_RUN=None ; tar_restart_end_OCE=None ; tar_restart_end_ICE=None 83 84 ContinueOnError=False ; ErrorCount=0 85 FileDirOCE=None ; FileDirATM=None ; FileDirICE=None ; FileDirSRF=None ; FileDirRUN=None 84 86 85 87 ## … … 87 89 ## --------------------------------- 88 90 # Default is float (full precision). Degrade the precision by using np.float32 89 # Restart file are always read at the full precision91 # Restart files are always read at the full precision 90 92 readPrec=float 91 93 … … 148 150 ## Reading config file 149 151 ## ------------------- 150 for Section in ['Config', 'Experiment', 'libIGCM', 'Files', 'Physics' ] : 151 if Section in config.keys () : 152 print ( f'\nReading [{Section}]' ) 153 for VarName in config[Section].keys() : 154 locals()[VarName] = config[Section][VarName] 155 exec ( f'{VarName} = wu.setBool ({VarName})' ) 156 exec ( f'{VarName} = wu.setNum ({VarName})' ) 157 exec ( f'{VarName} = wu.setNone ({VarName})' ) 158 exec ( f'wu.{VarName} = {VarName}' ) 159 print ( f' {VarName:21} set to : {locals()[VarName]}' ) 160 #exec ( f'del {VarName}' ) 152 # Each entry in the .ini file will create a Python variable with the same name 153 for Section in config.keys () : 154 print ( f'\nReading [{Section}]' ) 155 for VarName in config[Section].keys() : 156 locals()[VarName] = config[Section][VarName] 157 exec ( f'{VarName} = wu.setBool ({VarName})' ) 158 exec ( f'{VarName} = wu.setNum ({VarName})' ) 159 exec ( f'{VarName} = wu.setNone ({VarName})' ) 160 exec ( f'wu.{VarName} = {VarName}' ) 161 print ( f' {VarName:21} set to : {locals()[VarName]}' ) 161 162 162 163 print ( f'\nConfig file readed : {IniFile} ' ) … … 164 165 ## 165 166 ## Reading prec 166 if wu.unDefined ( 'readPrec' ):167 if not readPrec : 167 168 readPrec = np.float64 168 169 else : … … 171 172 if readPrec in [ "float16", "r2", "half" , "<class 'numpy.float16'>" ] : readPrec = np.float16 172 173 173 # #Some physical constants174 # Some physical constants 174 175 ## ======================= 175 if wu.unDefined ( 'Ra' ) : Ra = wu.Ra #-- Earth Radius (m) 176 if wu.unDefined ( 'Grav' ) : Grav = wu.Grav #-- Gravity (m^2/s 177 if wu.unDefined ( 'ICE_rho_ice' ) : ICE_rho_ice = wu.ICE_rho_ice #-- Ice volumic mass (kg/m3) in LIM3 178 if wu.unDefined ( 'ICE_rho_sno') : ICE_rho_sno = wu.ICE_rho_sno #-- Snow volumic mass (kg/m3) in LIM3 179 if wu.unDefined ( 'OCE_rho_liq' ) : OCE_rho_liq = wu.OCE_rho_liq #-- Ocean water volumic mass (kg/m3) in NEMO 180 if wu.unDefined ( 'ATM_rho' ) : ATM_rho = wu.ATM_rho #-- Water volumic mass in atmosphere (kg/m^3) 181 if wu.unDefined ( 'SRF_rho' ) : SRF_rho = wu.SRF_rho #-- Water volumic mass in surface reservoir (kg/m^3) 182 if wu.unDefined ( 'RUN_rho' ) : RUN_rho = wu.RUN_rho #-- Water volumic mass of rivers (kg/m^3) 183 if wu.unDefined ( 'ICE_rho_pnd' ) : ICE_rho_pnd = wu.ICE_rho_pnd #-- Water volumic mass in ice ponds (kg/m^3) 184 if wu.unDefined ( 'YearLength' ) : YearLength = wu.YearLength #-- Year length (s) 185 186 ## Set libIGCM and machine dependant values 187 ## ---------------------------------------- 188 if not 'Files' in config.keys () : config['Files'] = {} 189 190 config['Physics'] = { 'Ra':str(Ra), 'Grav':str(Grav), 'ICE_rho_ice':str(ICE_rho_ice), 'ICE_rho_sno':str(ICE_rho_sno), 191 'OCE_rho_liq':str(OCE_rho_liq), 'ATM_rho':str(ATM_rho), 'SRF_rho':str(SRF_rho), 'RUN_rho':str(RUN_rho)} 192 193 config['Config'] = { 'ContinueOnError':str(ContinueOnError), 'TestInterp':str(TestInterp), 'readPrec':str(readPrec) } 176 if not RA : RA = wu.RA #-- Earth Radius (m) 177 if not GRAV : GRAV = wu.GRAV #-- Gravity (m^2/s 178 if not ICE_RHO_ICE : ICE_RHO_ICE = wu.ICE_RHO_ICE #-- Ice volumic mass (kg/m3) in LIM3 179 if not ICE_RHO_SNO : ICE_RHO_SNO = wu.ICE_RHO_SNO #-- Snow volumic mass (kg/m3) in LIM3 180 if not OCE_RHO_LIQ : OCE_RHO_LIQ = wu.OCE_RHO_LIQ #-- Ocean water volumic mass (kg/m3) in NEMO 181 if not ATM_RHO : ATM_RHO = wu.ATM_RHO #-- Water volumic mass in atmosphere (kg/m^3) 182 if not SRF_RHO : SRF_RHO = wu.SRF_RHO #-- Water volumic mass in surface reservoir (kg/m^3) 183 if not RUN_RHO : RUN_RHO = wu.RUN_RHO #-- Water volumic mass of rivers (kg/m^3) 184 if not ICE_RHO_PND : ICE_RHO_PND = wu.ICE_RHO_PND #-- Water volumic mass in ice ponds (kg/m^3) 185 if not YEAR_LENGTH : YEAR_LENGTH = wu.YEAR_LENGTH #-- Year length (s) 186 187 if not 'Files' in config.keys () : config['Files'] = {} 188 if not 'Physics' in config.keys () : config['Physics'] = {} 189 if not 'Config' in config.keys () : config['Physics'] = {} 190 191 192 config['Physics'].update ( { 'RA':str(RA), 'GRAV':str(GRAV), 'ICE_RHO_ICE':str(ICE_RHO_ICE), 'ICE_RHO_SNO':str(ICE_RHO_SNO), 193 'OCE_RHO_LIQ':str(OCE_RHO_LIQ), 'ATM_RHO':str(ATM_RHO), 'SRF_RHO':str(SRF_RHO), 'RUN_RHO':str(RUN_RHO)} ) 194 195 config['Config'].update ( { 'ContinueOnError':str(ContinueOnError), 'TestInterp':str(TestInterp), 'readPrec':str(readPrec), 196 'Debug':str(Debug), 'Timer':str(Timer) } ) 194 197 195 198 ## -------------------------- … … 198 201 199 202 mm = libIGCM_sys.config ( TagName=TagName, SpaceName=SpaceName, ExperimentName=ExperimentName, JobName=JobName, User=User, Group=Group, 200 ARCHIVE= None, SCRATCHDIR=None, STORAGE=None, R_IN=None, R_OUT=None, R_FIG=None, rebuild=None, TmpDir=None,201 R_SAVE= None, R_FIGR=None, R_BUFR=None, R_BUF_KSH=None, REBUILD_DIR=None, POST_DIR=None)203 ARCHIVE=ARCHIVE, SCRATCHDIR=SCRATCHDIR, STORAGE=STORAGE, R_IN=R_IN, R_OUT=R_OUT, R_FIG=R_FIG, rebuild=rebuild, TmpDir=TmpDir, 204 R_SAVE=R_SAVE, R_FIGR=R_FIGR, R_BUFR=R_BUFR, R_BUF_KSH=R_BUF_KSH, REBUILD_DIR=REBUILD_DIR, POST_DIR=POST_DIR, L_EXP=L_EXP ) 202 205 globals().update(mm) 203 206 204 207 config['Files']['TmpDir'] = TmpDir 205 config['libIGCM'] = { 'ARCHIVE':ARCHIVE, 'STORAGE':STORAGE, 'TmpDir':TmpDir, 'R_IN':R_IN, 'rebuild':rebuild } 206 208 if not 'libIGCM' in config.keys () : config['libIGCM'] = {} 209 config['libIGCM'].update ( { 'ARCHIVE':str(ARCHIVE), 'STORAGE':str(STORAGE), 'TmpDir':str(TmpDir), 'R_IN':str(R_IN), 'rebuild':str(rebuild) } ) 210 211 ## Debuging and timer 212 Timer = wu.functools.partial (wu.Timer, debug=Debug, timer=Timer) 213 207 214 ## Defines begining and end of experiment 208 215 ## -------------------------------------- 209 if wu.unDefined ( 'DateBegin' ):216 if not DateBegin : 210 217 DateBegin = f'{YearBegin}0101' 211 218 config['Experiment']['DateBegin'] = str(DateBegin) … … 215 222 config['Experiment']['YearBegin'] = str(YearBegin) 216 223 217 if wu.unDefined ( 'DateEnd' ):224 if not DateEnd : 218 225 DateEnd = f'{YearEnd}1231' 219 226 config['Experiment']['DateEnd'] = str(DateEnd) … … 223 230 config['Experiment']['DateEnd'] = str(DateEnd) 224 231 225 if wu.unDefined ( 'PackFrequency' ) :232 if not PackFrequency : 226 233 PackFrequency = YearEnd - YearBegin + 1 227 234 config['Experiment']['PackFrequency'] = f'{PackFrequency}' … … 234 241 ## Output file with water budget diagnostics 235 242 ## ----------------------------------------- 236 if wu.unDefined ( 'FileOut' ):243 if not FileOut : 237 244 FileOut = f'CPL_waterbudget_{JobName}_{YearBegin}_{YearEnd}' 238 245 if ICO : … … 248 255 ## Useful functions 249 256 ## ---------------- 250 if readPrec == float : 251 def rprec (tab) : return tab 252 else : 253 def rprec (tab) : return tab.astype(readPrec).astype(float) 254 255 def kg2Sv (val, rho=ATM_rho) : 257 if repr(readPrec) == "<class 'numpy.float64'>" or readPrec == float : 258 def rprec (ptab) : 259 '''This version does nothing 260 261 rprec may be use to reduce floating precision when reading history files 262 ''' 263 return ptab 264 else : 265 def rprec (ptab) : 266 '''Returns float with a different precision''' 267 return ptab.astype(readPrec).astype(float) 268 269 def kg2Sv (val, rho=ATM_RHO) : 256 270 '''From kg to Sverdrup''' 257 271 return val/dtime_sec*1.0e-6/rho 258 272 259 def kg2myear (val, rho=ATM_ rho) :273 def kg2myear (val, rho=ATM_RHO) : 260 274 '''From kg to m/year''' 261 275 return val/ATM_aire_sea_tot/rho/NbYear 262 276 263 def var2prt (var, small=False, rho=ATM_rho) : 277 def var2prt (var, small=False, rho=ATM_RHO) : 278 '''Formats value for printing''' 264 279 if small : return var , kg2Sv(var, rho=rho)*1000., kg2myear(var, rho=rho)*1000 265 280 else : return var , kg2Sv(var, rho=rho) , kg2myear(var, rho=rho) 266 281 267 def prtFlux (Desc, var, Form='F', small=False, rho=ATM_rho, width=15) : 282 def prtFlux (Desc, var, Form='F', small=False, rho=ATM_RHO, width=15) : 283 '''Pretty print of formattd value''' 268 284 if small : 269 285 if Form in ['f', 'F'] : ff=" {:14.6e} kg | {:12.4f} mSv | {:12.4f} mm/year " … … 308 324 ## Set libIGCM directories 309 325 ## ----------------------- 310 if wu.unDefined ('R_OUT' ) : R_OUT = os.path.join ( ARCHIVE , 'IGCM_OUT' ) 311 if wu.unDefined ('R_BUF' ) : R_BUF = os.path.join ( SCRATCHDIR, 'IGCM_OUT' ) 312 if wu.unDefined ('L_EXP' ) : L_EXP = os.path.join (TagName, SpaceName, ExperimentName, JobName) 313 if wu.unDefined ('R_SAVE' ) : R_SAVE = os.path.join ( R_OUT, L_EXP ) 314 if wu.unDefined ('R_BUFR' ) : R_BUFR = os.path.join ( R_BUF, L_EXP ) 315 if wu.unDefined ('POST_DIR' ) : POST_DIR = os.path.join ( R_BUFR, 'Out' ) 316 if wu.unDefined ('REBUILD_DIR') : REBUILD_DIR = os.path.join ( R_BUFR, 'REBUILD' ) 317 if wu.unDefined ('R_BUF_KSH' ) : R_BUF_KSH = os.path.join ( R_BUFR, 'Out' ) 318 if wu.unDefined ('R_FIGR' ) : R_FIGR = os.path.join ( STORAGE, 'IGCM_OUT', L_EXP ) 319 320 config['libIGCM'].update ( { 'R_OUT':R_OUT, 'R_BUF':R_BUF, 'L_EXP':L_EXP, 'R_BUFR':R_BUFR, 'POST_DIR':POST_DIR, 321 'REBUILD_DIR':REBUILD_DIR, 'R_BUF_KSH':R_BUF_KSH, 'R_FIGR':R_FIGR, 'rebuild':rebuild } ) 326 if not R_OUT : R_OUT = os.path.join ( ARCHIVE , 'IGCM_OUT' ) 327 if not R_BUF : R_BUF = os.path.join ( SCRATCHDIR, 'IGCM_OUT' ) 328 if not L_EXP : L_EXP = os.path.join (TagName, SpaceName, ExperimentName, JobName) 329 if not R_SAVE : R_SAVE = os.path.join ( R_OUT, L_EXP ) 330 if not R_BUFR : R_BUFR = os.path.join ( R_BUF, L_EXP ) 331 if not POST_DIR : POST_DIR = os.path.join ( R_BUFR, 'Out' ) 332 if not REBUILD_DIR : REBUILD_DIR = os.path.join ( R_BUFR, 'REBUILD' ) 333 if not R_BUF_KSH : R_BUF_KSH = os.path.join ( R_BUFR, 'Out' ) 334 if not R_FIGR : R_FIGR = os.path.join ( STORAGE, 'IGCM_OUT', L_EXP ) 335 336 config['libIGCM'].update ( { 'R_OUT':R_OUT, 'R_BUF':R_BUF, 'L_EXP':L_EXP, 'R_BUFR':R_BUFR, 'R_SAVE':R_SAVE, 'POST_DIR':POST_DIR, 337 'REBUILD_DIR':REBUILD_DIR, 'R_BUF_KSH':R_BUF_KSH, 'R_FIGR':R_FIGR, 'rebuild':rebuild, 338 'YEAR_LENGTH':str(YEAR_LENGTH)} ) 322 339 323 340 ## Set directory to extract files 324 341 ## ------------------------------ 325 if wu.unDefined ( 'FileDir' ): FileDir = os.path.join ( TmpDir, f'WATER_{JobName}' )342 if not FileDir : FileDir = os.path.join ( TmpDir, f'WATER_{JobName}' ) 326 343 config['Files']['FileDir'] = FileDir 327 344 … … 329 346 330 347 ##- Set directories to rebuild ocean and ice restart files 331 if wu.unDefined ( 'FileDirOCE' ): FileDirOCE = os.path.join ( FileDir, 'OCE' )332 if wu.unDefined ( 'FileDirICE' ): FileDirICE = os.path.join ( FileDir, 'ICE' )348 if not FileDirOCE : FileDirOCE = os.path.join ( FileDir, 'OCE' ) 349 if not FileDirICE : FileDirICE = os.path.join ( FileDir, 'ICE' ) 333 350 if not os.path.exists ( FileDirOCE ) : os.mkdir ( FileDirOCE ) 334 351 if not os.path.exists ( FileDirICE ) : os.mkdir ( FileDirICE ) … … 358 375 dir_ATM_his = os.path.join ( R_SAVE, "ATM", FreqDir ) 359 376 config['Files']['dir_ATM_his'] = dir_ATM_his 360 if dir_SRF_his == None : 361 dir_SRF_his = os.path.join ( R_SAVE, "SRF", FreqDir ) 362 config['Files']['dir_SRF_his'] = dir_SRF_his 377 if SRF : 378 if dir_SRF_his == None : 379 dir_SRF_his = os.path.join ( R_SAVE, "SRF", FreqDir ) 380 config['Files']['dir_SRF_his'] = dir_SRF_his 363 381 if dir_OCE_his == None : 364 382 dir_OCE_his = os.path.join ( R_SAVE, "OCE", FreqDir ) … … 372 390 echo ( f'{dir_OCE_his}' ) 373 391 echo ( f'{dir_ICE_his}' ) 374 echo ( f'{dir_SRF_his}' ) 392 if SRF : 393 echo ( f'{dir_SRF_his}' ) 375 394 376 395 ##-- Creates files names 377 if wu.unDefined ( 'Period' ):396 if not Period : 378 397 if Freq == 'MO' : Period = f'{DateBegin}_{DateEnd}_1M' 379 398 if Freq == 'SE' : Period = f'SE_{DateBegin}_{DateEnd}_1M' … … 385 404 echo ( f'Period : {Period}' ) 386 405 387 if wu.unDefined ( 'FileCommon' ):406 if not FileCommon : 388 407 FileCommon = f'{JobName}_{Period}' 389 408 config['Files']['FileCommon'] = FileCommon 390 409 391 if wu.unDefined ( 'Title' ):410 if not Title : 392 411 Title = f'{JobName} : {Freq} : {DateBegin} - {DateEnd}' 393 412 config['Files']['Title'] = Title 394 413 echo ('\nOpen history files' ) 395 if wu.unDefined ( 'file_ATM_his' ):414 if not file_ATM_his : 396 415 if ATM_HIS == 'latlon' : 397 416 file_ATM_his = os.path.join ( dir_ATM_his, f'{FileCommon}_histmth.nc' ) … … 399 418 file_ATM_his = os.path.join ( dir_ATM_his, f'{FileCommon}_histmth_ico.nc' ) 400 419 config['Files']['file_ATM_his'] = file_ATM_his 401 if wu.unDefined ( 'file_SRF_his' ) : 402 if ATM_HIS == 'latlon' : 403 file_SRF_his = os.path.join ( dir_SRF_his, f'{FileCommon}_sechiba_history.nc' ) 404 if ATM_HIS == 'ico' : 405 file_SRF_his = os.path.join ( dir_SRF_his, f'{FileCommon}_sechiba_history_ico.nc' ) 406 config['Files']['file_SRF_his'] = file_SRF_his 407 408 if Routing == 'SIMPLE' : 409 if file_RUN_his == None : 420 if SRF : 421 if not file_SRF_his : 410 422 if ATM_HIS == 'latlon' : 411 file_ RUN_his = os.path.join ( dir_SRF_his, f'{FileCommon}_sechiba_history.nc' )423 file_SRF_his = os.path.join ( dir_SRF_his, f'{FileCommon}_sechiba_history.nc' ) 412 424 if ATM_HIS == 'ico' : 413 file_RUN_his = os.path.join ( dir_SRF_his, f'{FileCommon}_sechiba_history_ico.nc' ) 414 config['Files']['file_RUN_his'] = file_RUN_his 425 file_SRF_his = os.path.join ( dir_SRF_his, f'{FileCommon}_sechiba_history_ico.nc' ) 426 config['Files']['file_SRF_his'] = file_SRF_his 427 428 if Routing == 'SIMPLE' : 429 if file_RUN_his == None : 430 if ATM_HIS == 'latlon' : 431 file_RUN_his = os.path.join ( dir_SRF_his, f'{FileCommon}_sechiba_history.nc' ) 432 if ATM_HIS == 'ico' : 433 file_RUN_his = os.path.join ( dir_SRF_his, f'{FileCommon}_sechiba_history_ico.nc' ) 434 config['Files']['file_RUN_his'] = file_RUN_his 415 435 416 436 echo ( f'{file_ATM_his = }' ) 417 echo ( f'{file_SRF_his = }' ) 418 if Routing == 'SIMPLE' : echo ( f'{file_RUN_his = }' ) 437 if SRF : 438 echo ( f'{file_SRF_his = }' ) 439 if Routing == 'SIMPLE' : echo ( f'{file_RUN_his = }' ) 419 440 420 441 d_ATM_his = xr.open_dataset ( file_ATM_his, use_cftime=True, decode_times=True, decode_cf=True ).squeeze() 421 d_SRF_his = xr.open_dataset ( file_SRF_his, use_cftime=True, decode_times=True, decode_cf=True ).squeeze() 422 if Routing == 'SECHIBA' : d_RUN_his = d_SRF_his 423 if Routing == 'SIMPLE' : d_RUN_his = xr.open_dataset ( file_RUN_his, use_cftime=True, decode_times=True, decode_cf=True ).squeeze() 424 425 if wu.unDefined ('file_OCE_his' ) : 442 if SRF : 443 d_SRF_his = xr.open_dataset ( file_SRF_his, use_cftime=True, decode_times=True, decode_cf=True ).squeeze() 444 if Routing == 'SECHIBA' : d_RUN_his = d_SRF_his 445 if Routing == 'SIMPLE' : d_RUN_his = xr.open_dataset ( file_RUN_his, use_cftime=True, decode_times=True, decode_cf=True ).squeeze() 446 447 if not file_OCE_his : 426 448 file_OCE_his = os.path.join ( dir_OCE_his, f'{FileCommon}_grid_T.nc' ) 427 449 file_OCE_his = file_OCE_his 428 if wu.unDefined ('file_OCE_sca' ):450 if not file_OCE_sca : 429 451 file_OCE_sca = os.path.join ( dir_OCE_his, f'{FileCommon}_scalar.nc' ) 430 452 config['Files']['file_OCE_sca'] = file_OCE_sca 431 if wu.unDefined ('file_OCE_srf' ):453 if not file_OCE_srf : 432 454 file_OCE_srf = os.path.join ( dir_OCE_his, f'{FileCommon}_sbc.nc' ) 433 455 config['Files']['file_OCE_srf'] = file_OCE_srf 434 if wu.unDefined ( 'file_ICE_hi' ):456 if not file_ICE_his : 435 457 file_ICE_his = os.path.join ( dir_ICE_his, f'{FileCommon}_icemod.nc' ) 436 458 config['Files']['file_ICE_his'] = file_ICE_his … … 440 462 #d_OCE_srf = xr.open_dataset ( file_OCE_srf, use_cftime=True, decode_times=True, decode_cf=True ).squeeze() 441 463 d_ICE_his = xr.open_dataset ( file_ICE_his, use_cftime=True, decode_times=True, decode_cf=True ).squeeze() 442 if NEMO == '3.6' : d_ICE_his = d_ICE_his.rename ( {'y_grid_T':'y', 'x_grid_T':'x'} )464 if NEMO == '3.6' : d_ICE_his = d_ICE_his.rename ( {'y_grid_T':'y', 'x_grid_T':'x'} ) 443 465 444 466 echo ( f'{file_OCE_his = }' ) … … 461 483 462 484 # Number of years 463 NbYear = dtime_sec / Y earLength485 NbYear = dtime_sec / YEAR_LENGTH 464 486 465 487 ## Write the full configuration … … 478 500 ATM_fsic = lmdz.geo2point ( rprec (d_ATM_his ['fract_sic'][0]), dim1D='cell' ) 479 501 ATM_flic = lmdz.geo2point ( rprec (d_ATM_his ['fract_lic'][0]), dim1D='cell' ) 480 SRF_lat = lmdz.geo2point ( rprec (d_SRF_his ['lat'])+0*rprec (d_SRF_his ['lon']), dim1D='cell' ) 481 SRF_lon = lmdz.geo2point ( 0*rprec (d_SRF_his ['lat'])+ rprec (d_SRF_his ['lon']), dim1D='cell' ) 482 SRF_aire = lmdz.geo2point ( rprec (d_SRF_his ['Areas']) * rprec (d_SRF_his ['Contfrac']), dim1D='cell', cumulPoles=True ) 483 SRF_areas = lmdz.geo2point ( rprec (d_SRF_his ['Areas']) , dim1D='cell', cumulPoles=True ) 484 SRF_contfrac = lmdz.geo2point ( rprec (d_SRF_his ['Contfrac']), dim1D='cell' ) 502 if SRF : 503 SRF_lat = lmdz.geo2point ( rprec (d_SRF_his ['lat'])+0*rprec (d_SRF_his ['lon']), dim1D='cell' ) 504 SRF_lon = lmdz.geo2point ( 0*rprec (d_SRF_his ['lat'])+ rprec (d_SRF_his ['lon']), dim1D='cell' ) 505 SRF_aire = lmdz.geo2point ( rprec (d_SRF_his ['Areas']) * rprec (d_SRF_his ['Contfrac']), dim1D='cell', cumulPoles=True ) 506 SRF_areas = lmdz.geo2point ( rprec (d_SRF_his ['Areas']) , dim1D='cell', cumulPoles=True ) 507 SRF_contfrac = lmdz.geo2point ( rprec (d_SRF_his ['Contfrac']), dim1D='cell' ) 508 485 509 if ICO : 486 510 if ATM_HIS == 'latlon' : … … 508 532 ATM_flic = rprec (d_ATM_his ['fract_lic'][0]) 509 533 510 if SRF_HIS == 'latlon' : 511 echo ( 'SRF areas and fractions on latlon grid' ) 512 if 'lat_domain_landpoints_out' in d_SRF_his : 513 SRF_lat = lmdz.geo2point ( rprec (d_SRF_his ['lat_domain_landpoints_out'])+0*rprec (d_SRF_his ['lon_domain_landpoints_out']), dim1D='cell' ) 514 SRF_lon = lmdz.geo2point ( 0*rprec (d_SRF_his ['lat_domain_landpoints_out'])+ rprec (d_SRF_his ['lon_domain_landpoints_out']), dim1D='cell' ) 515 else : 516 if 'lat_domain_landpoints_out' in d_SRF_his : 517 SRF_lat = lmdz.geo2point ( rprec (d_SRF_his ['lat_dom_out'])+0*rprec (d_SRF_his ['lon_dom_out']), dim1D='cell' ) 518 SRF_lon = lmdz.geo2point ( 0*rprec (d_SRF_his ['lat_dom_out'])+ rprec (d_SRF_his ['lon_dom_out']), dim1D='cell' ) 519 else : 520 SRF_lat = lmdz.geo2point ( rprec (d_SRF_his ['lat'])+0*rprec (d_SRF_his ['lon']), dim1D='cell' ) 521 SRF_lon = lmdz.geo2point ( 0*rprec (d_SRF_his ['lat'])+ rprec (d_SRF_his ['lon']), dim1D='cell' ) 522 523 SRF_areas = lmdz.geo2point ( rprec (d_SRF_his ['Areas'] ) , dim1D='cell', cumulPoles=True ) 524 SRF_areafrac = lmdz.geo2point ( rprec (d_SRF_his ['AreaFrac']) , dim1D='cell', cumulPoles=True ) 525 SRF_contfrac = lmdz.geo2point ( rprec (d_SRF_his ['Contfrac']) , dim1D='cell', cumulPoles=True ) 526 SRF_aire = SRF_areafrac 534 if SRF : 535 if SRF_HIS == 'latlon' : 536 echo ( 'SRF areas and fractions on latlon grid' ) 537 if 'lat_domain_landpoints_out' in d_SRF_his : 538 SRF_lat = lmdz.geo2point ( rprec (d_SRF_his ['lat_domain_landpoints_out'])+0*rprec (d_SRF_his ['lon_domain_landpoints_out']), dim1D='cell' ) 539 SRF_lon = lmdz.geo2point ( 0*rprec (d_SRF_his ['lat_domain_landpoints_out'])+ rprec (d_SRF_his ['lon_domain_landpoints_out']), dim1D='cell' ) 540 else : 541 if 'lat_domain_landpoints_out' in d_SRF_his : 542 SRF_lat = lmdz.geo2point ( rprec (d_SRF_his ['lat_dom_out'])+0*rprec (d_SRF_his ['lon_dom_out']), dim1D='cell' ) 543 SRF_lon = lmdz.geo2point ( 0*rprec (d_SRF_his ['lat_dom_out'])+ rprec (d_SRF_his ['lon_dom_out']), dim1D='cell' ) 544 else : 545 SRF_lat = lmdz.geo2point ( rprec (d_SRF_his ['lat'])+0*rprec (d_SRF_his ['lon']), dim1D='cell' ) 546 SRF_lon = lmdz.geo2point ( 0*rprec (d_SRF_his ['lat'])+ rprec (d_SRF_his ['lon']), dim1D='cell' ) 547 548 SRF_areas = lmdz.geo2point ( rprec (d_SRF_his ['Areas'] ) , dim1D='cell', cumulPoles=True ) 549 SRF_areafrac = lmdz.geo2point ( rprec (d_SRF_his ['AreaFrac']) , dim1D='cell', cumulPoles=True ) 550 SRF_contfrac = lmdz.geo2point ( rprec (d_SRF_his ['Contfrac']) , dim1D='cell', cumulPoles=True ) 551 SRF_aire = SRF_areafrac 527 552 528 if SRF_HIS == 'ico' : 529 echo ( 'SRF areas and fractions on latlon grid' ) 530 SRF_lat = rprec (d_SRF_his ['lat'] ) 531 SRF_lon = rprec (d_SRF_his ['lon'] ) 532 SRF_areas = rprec (d_SRF_his ['Areas'] ) 533 SRF_contfrac = rprec (d_SRF_his ['Contfrac']) 534 SRF_aire = SRF_areas * SRF_contfrac 553 if SRF_HIS == 'ico' : 554 echo ( 'SRF areas and fractions on latlon grid' ) 555 SRF_lat = rprec (d_SRF_his ['lat'] ) 556 SRF_lon = rprec (d_SRF_his ['lon'] ) 557 SRF_areas = rprec (d_SRF_his ['Areas'] ) 558 SRF_contfrac = rprec (d_SRF_his ['Contfrac']) 559 SRF_aire = SRF_areas * SRF_contfrac 560 535 561 ATM_fsea = ATM_foce + ATM_fsic 536 562 ATM_flnd = ATM_fter + ATM_flic … … 592 618 return SRF_flux_int 593 619 594 def LIC_flux_int (flux) :595 '''Integrate (* time * surface) flux on land ice grid'''596 LIC_flux_int = wu.Psum ( (flux * dtime_per_sec * ATM_aire_flic).to_masked_array().ravel() )597 return LIC_flux_int620 # def LIC_flux_int (flux) : 621 # '''Integrate (* time * surface) flux on land ice grid''' 622 # LIC_flux_int = wu.Psum ( (flux * dtime_per_sec * ATM_aire_flic).to_masked_array().ravel() ) 623 # return LIC_flux_int 598 624 599 625 # def OCE_stock_int (stock) : … … 628 654 629 655 ATM_aire_tot = ONE_stock_int (ATM_aire) 630 SRF_aire_tot = ONE_stock_int (SRF_aire) 656 if SRF : 657 SRF_aire_tot = ONE_stock_int (SRF_aire) 631 658 OCE_aire_tot = ONE_stock_int (OCE_aire) 632 659 ICE_aire_tot = ONE_stock_int (ICE_aire) … … 724 751 ATM_wemp_sea = ATM_wevap_sic - ATM_wprecip_oce 725 752 726 if RUN_HIS == 'latlon' : 727 echo ( f'RUN costalflow Grille LATLON' ) 728 if TestInterp : 729 echo ( f'RUN runoff TestInterp' ) 730 RUN_runoff = lmdz.geo2point ( rprec (d_RUN_his ['runoff_contfrac_interp'] ) , dim1D='cell' ) 731 RUN_drainage = lmdz.geo2point ( rprec (d_RUN_his ['drainage_contfrac_interp']) , dim1D='cell' ) 732 else : 733 echo ( f'RUN runoff' ) 734 RUN_runoff = lmdz.geo2point ( rprec (d_RUN_his ['runoff'] ), dim1D='cell' ) 735 RUN_drainage = lmdz.geo2point ( rprec (d_RUN_his ['drainage'] ), dim1D='cell' ) 753 if SRF : 754 if RUN_HIS == 'latlon' : 755 echo ( f'RUN costalflow Grille LATLON' ) 756 if TestInterp : 757 echo ( f'RUN runoff TestInterp' ) 758 RUN_runoff = lmdz.geo2point ( rprec (d_RUN_his ['runoff_contfrac_interp'] ) , dim1D='cell' ) 759 RUN_drainage = lmdz.geo2point ( rprec (d_RUN_his ['drainage_contfrac_interp']) , dim1D='cell' ) 760 else : 761 echo ( f'RUN runoff' ) 762 RUN_runoff = lmdz.geo2point ( rprec (d_RUN_his ['runoff'] ), dim1D='cell' ) 763 RUN_drainage = lmdz.geo2point ( rprec (d_RUN_his ['drainage'] ), dim1D='cell' ) 764 765 RUN_coastalflow = lmdz.geo2point ( rprec (d_RUN_his ['coastalflow'] ), dim1D='cell' ) 766 RUN_riverflow = lmdz.geo2point ( rprec (d_RUN_his ['riverflow'] ), dim1D='cell' ) 767 RUN_riversret = lmdz.geo2point ( rprec (d_RUN_his ['riversret'] ), dim1D='cell' ) 768 RUN_coastalflow_cpl = lmdz.geo2point ( rprec (d_RUN_his ['coastalflow_cpl']), dim1D='cell' ) 769 RUN_riverflow_cpl = lmdz.geo2point ( rprec (d_RUN_his ['riverflow_cpl'] ), dim1D='cell' ) 736 770 737 RUN_coastalflow = lmdz.geo2point ( rprec (d_RUN_his ['coastalflow'] ), dim1D='cell' ) 738 RUN_riverflow = lmdz.geo2point ( rprec (d_RUN_his ['riverflow'] ), dim1D='cell' ) 739 RUN_riversret = lmdz.geo2point ( rprec (d_RUN_his ['riversret'] ), dim1D='cell' ) 740 RUN_coastalflow_cpl = lmdz.geo2point ( rprec (d_RUN_his ['coastalflow_cpl']), dim1D='cell' ) 741 RUN_riverflow_cpl = lmdz.geo2point ( rprec (d_RUN_his ['riverflow_cpl'] ), dim1D='cell' ) 742 743 if RUN_HIS == 'ico' : 744 echo ( f'RUN costalflow Grille ICO' ) 745 RUN_coastalflow = rprec (d_RUN_his ['coastalflow']) 746 RUN_riverflow = rprec (d_RUN_his ['riverflow'] ) 747 RUN_runoff = rprec (d_RUN_his ['runoff'] ) 748 RUN_drainage = rprec (d_RUN_his ['drainage'] ) 749 RUN_riversret = rprec (d_RUN_his ['riversret'] ) 750 751 RUN_coastalflow_cpl = rprec (d_RUN_his ['coastalflow_cpl']) 752 RUN_riverflow_cpl = rprec (d_RUN_his ['riverflow_cpl'] ) 753 754 ## Correcting units of SECHIBA variables 755 def mmd2SI ( Var ) : 756 '''Change unit from mm/d or m^3/s to kg/s if needed''' 757 if 'units' in VarT.attrs : 758 if VarT.attrs['units'] in ['m^3/s', 'm3/s', 'm3.s-1',] : 759 VarT.values = VarT.values * ATM_rho ; VarT.attrs['units'] = 'kg/s' 760 if VarT.attrs['units'] == 'mm/d' : 761 VarT.values = VarT.values * ATM_rho * (1e-3/86400.) ; VarT.attrs['units'] = 'kg/s' 762 if VarT.attrs['units'] in ['m^3', 'm3', ] : 763 VarT.values = VarT.values * ATM_rho ; VarT.attrs['units'] = 'kg' 764 765 for var in [ 'runoff', 'drainage', 'riversret', 'coastalflow', 'riverflow', 'coastalflow_cpl', 'riverflow_cpl' ] : 766 VarT = locals()['RUN_' + var] 767 mmd2SI (VarT) 768 769 #for var in ['evap', 'snowf', 'subli', 'transpir', 'rain', 'emp' ] : 770 # VarT = locals()['SRF_' + var] 771 # mmd2SI (VarT) 772 echo ( f'RUN input' ) 773 RUN_input = RUN_runoff + RUN_drainage 774 RUN_output = RUN_coastalflow + RUN_riverflow 775 771 if RUN_HIS == 'ico' : 772 echo ( f'RUN costalflow Grille ICO' ) 773 RUN_coastalflow = rprec (d_RUN_his ['coastalflow']) 774 RUN_riverflow = rprec (d_RUN_his ['riverflow'] ) 775 RUN_runoff = rprec (d_RUN_his ['runoff'] ) 776 RUN_drainage = rprec (d_RUN_his ['drainage'] ) 777 RUN_riversret = rprec (d_RUN_his ['riversret'] ) 778 779 RUN_coastalflow_cpl = rprec (d_RUN_his ['coastalflow_cpl']) 780 RUN_riverflow_cpl = rprec (d_RUN_his ['riverflow_cpl'] ) 781 782 ## Correcting units of SECHIBA variables 783 def mmd2SI ( Var ) : 784 '''Change unit from mm/d or m^3/s to kg/s if needed''' 785 if 'units' in VarT.attrs : 786 if VarT.attrs['units'] in ['m^3/s', 'm3/s', 'm3.s-1',] : 787 VarT.values = VarT.values * ATM_RHO ; VarT.attrs['units'] = 'kg/s' 788 if VarT.attrs['units'] == 'mm/d' : 789 VarT.values = VarT.values * ATM_RHO * (1e-3/86400.) ; VarT.attrs['units'] = 'kg/s' 790 if VarT.attrs['units'] in ['m^3', 'm3', ] : 791 VarT.values = VarT.values * ATM_RHO ; VarT.attrs['units'] = 'kg' 792 793 for var in [ 'runoff', 'drainage', 'riversret', 'coastalflow', 'riverflow', 'coastalflow_cpl', 'riverflow_cpl' ] : 794 VarT = locals()['RUN_' + var] 795 mmd2SI (VarT) 796 797 #for var in ['evap', 'snowf', 'subli', 'transpir', 'rain', 'emp' ] : 798 # VarT = locals()['SRF_' + var] 799 # mmd2SI (VarT) 800 echo ( f'RUN input' ) 801 RUN_input = RUN_runoff + RUN_drainage 802 RUN_output = RUN_coastalflow + RUN_riverflow 803 776 804 echo ( f'ATM flw_wbilo' ) 777 805 ATM_flx_wbilo = ATM_flux_int ( ATM_wbilo ) … … 790 818 ATM_flx_fqfonte = ATM_flux_int ( ATM_fqfonte ) 791 819 792 LIC_flx_calving = LIC_flux_int ( ATM_fqcalving )793 LIC_flx_fqfonte = LIC_flux_int ( ATM_fqfonte )820 LIC_flx_calving = ATM_flux_int ( ATM_fqcalving ) 821 LIC_flx_fqfonte = ATM_flux_int ( ATM_fqfonte ) 794 822 795 823 ATM_flx_precip = ATM_flux_int ( ATM_precip ) … … 798 826 ATM_flx_runlic = ATM_flux_int ( ATM_runofflic ) 799 827 800 LIC_flx_precip = LIC_flux_int ( ATM_precip )801 LIC_flx_snowf = LIC_flux_int ( ATM_snowf )802 LIC_flx_evap = LIC_flux_int ( ATM_evap )803 LIC_flx_runlic = LIC_flux_int ( ATM_runofflic )828 #LIC_flx_precip = LIC_flux_int ( ATM_precip ) 829 #LIC_flx_snowf = LIC_flux_int ( ATM_snowf ) 830 #LIC_flx_evap = LIC_flux_int ( ATM_evap ) 831 #LIC_flx_runlic = LIC_flux_int ( ATM_runofflic ) 804 832 805 833 ATM_flx_wrain_ter = ATM_flux_int ( ATM_wrain_ter ) … … 833 861 ATM_flx_emp = ATM_flux_int ( ATM_emp ) 834 862 835 RUN_flx_coastal = ONE_flux_int ( RUN_coastalflow) 836 RUN_flx_river = ONE_flux_int ( RUN_riverflow ) 837 RUN_flx_coastal_cpl = ONE_flux_int ( RUN_coastalflow_cpl) 838 RUN_flx_river_cpl = ONE_flux_int ( RUN_riverflow_cpl ) 839 RUN_flx_drainage = SRF_flux_int ( RUN_drainage ) 840 RUN_flx_riversret = SRF_flux_int ( RUN_riversret ) 841 RUN_flx_runoff = SRF_flux_int ( RUN_runoff ) 842 RUN_flx_input = SRF_flux_int ( RUN_input ) 843 RUN_flx_output = ONE_flux_int ( RUN_output ) 844 845 RUN_flx_bil = ONE_flux_int ( RUN_input - RUN_output) 846 RUN_flx_rivcoa = ONE_flux_int ( RUN_coastalflow + RUN_riverflow) 847 863 if SRF : 864 RUN_flx_coastal = ONE_flux_int ( RUN_coastalflow) 865 RUN_flx_river = ONE_flux_int ( RUN_riverflow ) 866 RUN_flx_coastal_cpl = ONE_flux_int ( RUN_coastalflow_cpl) 867 RUN_flx_river_cpl = ONE_flux_int ( RUN_riverflow_cpl ) 868 RUN_flx_drainage = SRF_flux_int ( RUN_drainage ) 869 RUN_flx_riversret = SRF_flux_int ( RUN_riversret ) 870 RUN_flx_runoff = SRF_flux_int ( RUN_runoff ) 871 RUN_flx_input = SRF_flux_int ( RUN_input ) 872 RUN_flx_output = ONE_flux_int ( RUN_output ) 873 874 RUN_flx_bil = ONE_flux_int ( RUN_input - RUN_output) 875 RUN_flx_rivcoa = ONE_flux_int ( RUN_coastalflow + RUN_riverflow) 876 848 877 prtFlux ('wbilo_oce ', ATM_flx_wbilo_oce , 'f' ) 849 878 prtFlux ('wbilo_sic ', ATM_flx_wbilo_sic , 'f' ) … … 871 900 prtFlux ('ERROR emp ', ATM_flx_wemp - ATM_flx_emp , 'e', True ) 872 901 873 874 echo ( '\n====================================================================================' )875 echo ( f'-- RUNOFF Fluxes -- {Title} ' )876 prtFlux ('coastalflow ', RUN_flx_coastal , 'f' )877 prtFlux ('riverflow ', RUN_flx_river , 'f' )878 prtFlux ('coastal_cpl ', RUN_flx_coastal_cpl, 'f' )879 prtFlux ('riverf_cpl ', RUN_flx_river_cpl , 'f' )880 prtFlux ('river+coastal ', RUN_flx_rivcoa , 'f' )881 prtFlux ('drainage ', RUN_flx_drainage , 'f' )882 prtFlux ('riversret ', RUN_flx_riversret , 'f' )883 prtFlux ('runoff ', RUN_flx_runoff , 'f' )884 prtFlux ('river in ', RUN_flx_input , 'f' )885 prtFlux ('river out ', RUN_flx_output , 'f' )886 prtFlux ('river bil ', RUN_flx_bil , 'f' )902 if SRF : 903 echo ( '\n====================================================================================' ) 904 echo ( f'-- RUNOFF Fluxes -- {Title} ' ) 905 prtFlux ('coastalflow ', RUN_flx_coastal , 'f' ) 906 prtFlux ('riverflow ', RUN_flx_river , 'f' ) 907 prtFlux ('coastal_cpl ', RUN_flx_coastal_cpl, 'f' ) 908 prtFlux ('riverf_cpl ', RUN_flx_river_cpl , 'f' ) 909 prtFlux ('river+coastal ', RUN_flx_rivcoa , 'f' ) 910 prtFlux ('drainage ', RUN_flx_drainage , 'f' ) 911 prtFlux ('riversret ', RUN_flx_riversret , 'f' ) 912 prtFlux ('runoff ', RUN_flx_runoff , 'f' ) 913 prtFlux ('river in ', RUN_flx_input , 'f' ) 914 prtFlux ('river out ', RUN_flx_output , 'f' ) 915 prtFlux ('river bil ', RUN_flx_bil , 'f' ) 887 916 888 917 echo ( '\n====================================================================================' ) … … 904 933 OCE_wfxsub = rprec (d_OCE_his['vfxsub']) ; OCE_mas_wfxsub = OCE_flux_int ( OCE_wfxsub ) 905 934 if NEMO == 3.6 : 906 OCE_wfxice = rprec (d_OCE_his['vfxice'])/86400.*ICE_ rho_ice; OCE_mas_wfxice = OCE_flux_int ( OCE_wfxice )907 OCE_wfxsnw = rprec (d_OCE_his['vfxsnw'])/86400.*ICE_ rho_sno; OCE_mas_wfxsnw = OCE_flux_int ( OCE_wfxsnw )908 OCE_wfxsub = rprec (d_OCE_his['vfxsub'])/86400.*ICE_ rho_sno; OCE_mas_wfxsub = OCE_flux_int ( OCE_wfxsub )935 OCE_wfxice = rprec (d_OCE_his['vfxice'])/86400.*ICE_RHO_ICE ; OCE_mas_wfxice = OCE_flux_int ( OCE_wfxice ) 936 OCE_wfxsnw = rprec (d_OCE_his['vfxsnw'])/86400.*ICE_RHO_SNO ; OCE_mas_wfxsnw = OCE_flux_int ( OCE_wfxsnw ) 937 OCE_wfxsub = rprec (d_OCE_his['vfxsub'])/86400.*ICE_RHO_SNO ; OCE_mas_wfxsub = OCE_flux_int ( OCE_wfxsub ) 909 938 # Additional checks 910 939 OCE_evap_oce = rprec (d_OCE_his['evap_ao_cea']) ; OCE_mas_evap_oce = OCE_flux_int ( OCE_evap_oce ) … … 920 949 if NEMO == 3.6 : 921 950 ICE_wfxpnd = 0.0 ; ICE_mas_wfxpnd = 0.0 922 ICE_wfxsnw_sub = rprec (d_ICE_his['vfxsub'])/86400.*ICE_ rho_sno; ICE_mas_wfxsnw_sub = OCE_flux_int ( ICE_wfxsnw_sub )923 ICE_wfxsnw_pre = rprec (d_ICE_his['vfxspr'])/86400.*ICE_ rho_sno; ICE_mas_wfxsnw_pre = OCE_flux_int ( ICE_wfxsnw_pre )951 ICE_wfxsnw_sub = rprec (d_ICE_his['vfxsub'])/86400.*ICE_RHO_SNO ; ICE_mas_wfxsnw_sub = OCE_flux_int ( ICE_wfxsnw_sub ) 952 ICE_wfxsnw_pre = rprec (d_ICE_his['vfxspr'])/86400.*ICE_RHO_SNO ; ICE_mas_wfxsnw_pre = OCE_flux_int ( ICE_wfxsnw_pre ) 924 953 925 954 OCE_wfcorr = rprec (d_OCE_his['wfcorr']) ; OCE_mas_wfcorr = OCE_flux_int ( OCE_wfcorr ) … … 974 1003 975 1004 prtFlux ( 'wbilo sea ', ATM_flux_int (ATM_wbilo_sea), 'e', ) 976 prtFlux ( 'costalflow ', ONE_flux_int (RUN_coastalflow), 'e', ) 977 prtFlux ( 'riverflow ', RUN_flx_river , 'e', ) 978 prtFlux ( 'costalflow ', RUN_flx_coastal, 'e', ) 979 prtFlux ( 'runoff ', RUN_flx_river+RUN_flx_coastal, 'e', ) 980 981 ATM_to_OCE = ATM_flux_int (ATM_wbilo_sea) - RUN_flx_river - RUN_flx_coastal - ATM_flx_calving 1005 if SRF : 1006 prtFlux ( 'costalflow ', ONE_flux_int (RUN_coastalflow), 'e', ) 1007 prtFlux ( 'riverflow ', RUN_flx_river , 'e', ) 1008 prtFlux ( 'costalflow ', RUN_flx_coastal, 'e', ) 1009 prtFlux ( 'runoff ', RUN_flx_river+RUN_flx_coastal, 'e', ) 1010 1011 #ATM_to_OCE = ATM_flux_int (ATM_wbilo_sea) - RUN_flx_river - RUN_flx_coastal - ATM_flx_calving 1012 ATM_to_OCE = ATM_flux_int (ATM_wbilo_sea) - ATM_flx_river - ATM_flx_coastal - ATM_flx_calving 982 1013 #OCE_from_ATM = -OCE_mas_emp_oce - OCE_mas_emp_ice + OCE_mas_runoffs + OCE_mas_iceberg + OCE_mas_calving + OCE_mas_iceshelf 983 1014 OCE_from_ATM = OCE_mas_all … … 985 1016 prtFlux ( 'ATM_to_OCE ', ATM_to_OCE , 'e', True ) 986 1017 prtFlux ( 'OCE_from_ATM', OCE_from_ATM, 'e', True ) 1018 1019 echo ( ' ' ) 1020 echo ( f'{Title = }' ) 1021 1022 echo ( 'SVN Information' ) 1023 for kk in SVN.keys(): 1024 print ( SVN[kk] ) -
TOOLS/WATER_BUDGET/OCE_waterbudget.py
r6651 r6665 22 22 ### 23 23 ## Import system modules 24 import sys, os, shutil#, subprocess, platform 25 import configparser, re 24 import sys 25 import os 26 import subprocess 27 import configparser 26 28 from pathlib import Path 27 29 28 30 ## Import needed scientific modules 29 import numpy as np, xarray as xr 30 31 # Check python version 32 if sys.version_info < (3, 8, 0) : 33 print ( f'Python version : {platform.python_version()}' ) 34 raise Exception ( "Minimum Python version is 3.8" ) 31 import numpy as np 32 import xarray as xr 35 33 36 34 ## Import local module 37 35 import WaterUtils as wu 38 36 import libIGCM_sys 39 import nemo, lmdz 40 41 from WaterUtils import VarInt, Rho, Ra, Grav, ICE_rho_ice, ICE_rho_sno, OCE_rho_liq, ATM_rho, SRF_rho, RUN_rho, ICE_rho_pnd, YearLength 37 import nemo 38 39 from WaterUtils import RA, GRAV, ICE_RHO_ICE, ICE_RHO_SNO, OCE_RHO_LIQ, ATM_RHO, \ 40 SRF_RHO, RUN_RHO, ICE_RHO_PND, YEAR_LENGTH 42 41 43 42 ## Creates parser for reading .ini input file … … 48 47 ## Experiment parameters 49 48 ## --------------------- 50 ATM=None ; ATM_HIS='latlon' ; SRF_HIS='latlon' ; RUN_HIS='latlon' ; ORCA=None ; NEMO=None ; OCE_relax=False 51 OCE_icb=False ; Coupled=False ; Routing=None ; TestInterp=None 52 TarRestartPeriod_beg=None ; TarRestartPeriod_end=None ; Comment=None ; Period=None ; Title=None 49 JobName=None ; TagName=None ; ExperimentName=None ; SpaceName=None 50 SRF=True ; RUN=True 51 ATM=None ; ATM_HIS='latlon' ; SRF_HIS='latlon' ; RUN_HIS='latlon' ; ORCA=None 52 NEMO=None ; OCE_relax=False 53 OCE_icb=False ; Coupled=False ; Rsouting=None ; TestInterp=None 54 TarRestartPeriod_beg=None ; TarRestartPeriod_end=None ; Comment=None 55 Period=None ; Title=None 53 56 YearBegin=None ; YearEnd=None ; DateBegin=None ; DateEnd=None 57 Freq=None ; libIGCM=None ; User=None; Group=None ; Routing=None 58 PackFrequency = None ; FreqDir=None 59 60 Timer=False ; Debug=False 54 61 55 62 ## 56 ARCHIVE=None ; STORAGE=None ; SCRATCHDIR=None ; R_IN=None ; rebuild=None ; TmpDir=None 57 FileDir=None ; FileOut=None 63 ARCHIVE=None ; STORAGE=None ; SCRATCHDIR=None ; R_IN=None 64 TmpDir=None ; FileDir=None ; FileOut=None ; R_OUT=None ; R_FIG=None 65 R_FIGR=None ; R_BUF=None ; R_BUFR=None ; R_SAVE=None 66 R_BUF_KSH=None ; POST_DIR=None ; L_EXP=None 67 58 68 dir_ATM_his=None ; dir_SRF_his=None ; dir_OCE_his=None ; dir_ICE_his=None 59 69 FileCommon=None ; file_ATM_his=None ; file_SRF_his=None ; file_RUN_his=None 60 70 file_OCE_his=None ; file_ICE_his=None ; file_OCE_sca=None 61 tar_restart_beg=None ; tar_restart_end=None ; file_ATM_beg=None ; file_ATM_end=None ; file_DYN_beg=None 71 tar_restart_beg=None ; tar_restart_end=None ; file_ATM_beg=None 72 file_ATM_end=None ; file_DYN_beg=None 62 73 file_DYN_end=None ; file_SRF_beg=None ; file_SRF_end=None 63 74 file_RUN_beg=None ; file_RUN_end=None ; file_RUN_end=None ; file_OCE_beg=None 64 75 file_ICE_beg=None ; file_OCE_beg=None 65 76 file_OCE_end=None ; file_ICE_beg=None ; file_OCE_end=None ; file_ICE_end=None 77 TarRestartDate_beg=None ; TarRestartDate_end=None 78 file_DYN_aire=None 66 79 tar_restart_beg_ATM=None ; tar_restart_beg_DYN=None ; tar_restart_beg_SRF=None 67 80 tar_restart_beg_RUN=None ; tar_restart_beg_OCE=None ; tar_restart_beg_ICE=None 68 81 tar_restart_end_ATM=None ; tar_restart_end_DYN=None ; tar_restart_end_SRF=None 69 82 tar_restart_end_RUN=None ; tar_restart_end_OCE=None ; tar_restart_end_ICE=None 70 ContinueOnError=False ; ErrorCount=0 83 ContinueOnError=False ; ErrorCount=0 ; SortIco = False 84 85 d_OCE_beg=None ; d_OCE_end=None 86 87 FileDirOCE=None ; FileDirATM=None ; FileDirICE=None ; FileDirSRF=None ; FileDirRUN=None 88 89 rebuild=None ; REBUILD_DIR=None 71 90 72 91 ## … … 74 93 ## --------------------------------- 75 94 # Default is float (full precision). Degrade the precision by using np.float32 76 # Restart file are always read at the full precision95 # Restart files are always read at the full precision 77 96 readPrec=float 78 97 … … 88 107 if 'full' in IniFile : FullIniFile = IniFile 89 108 else : FullIniFile = 'full_' + IniFile 90 109 91 110 print ("Input file : ", IniFile ) 92 111 config.read (IniFile) … … 109 128 MyReader = configparser.ConfigParser (interpolation=configparser.ExtendedInterpolation() ) 110 129 MyReader.optionxform = str # To keep capitals 111 130 112 131 MyReader.read (ConfigCard) 113 132 … … 120 139 exec ( f'wu.{VarName} = {VarName}' ) 121 140 print ( f' {VarName:21} set to : {locals()[VarName]:}' ) 122 141 123 142 for VarName in ['PackFrequency'] : 124 143 if VarName in MyReader['Post'].keys() : … … 134 153 ## Reading config file 135 154 ## ------------------- 136 for Section in ['Config', 'Experiment', 'libIGCM', 'Files', 'Physics' ] : 137 if Section in config.keys () : 138 139 140 141 142 143 144 145 155 # Each entry in the .ini file will create a Python variable with the same name 156 for Section in config.keys () : 157 print ( f'\nReading [{Section}]' ) 158 for VarName in config[Section].keys() : 159 locals()[VarName] = config[Section][VarName] 160 exec ( f'{VarName} = wu.setBool ({VarName})' ) 161 exec ( f'{VarName} = wu.setNum ({VarName})' ) 162 exec ( f'{VarName} = wu.setNone ({VarName})' ) 163 exec ( f'wu.{VarName} = {VarName}' ) 164 print ( f' {VarName:21} set to : {locals()[VarName]}' ) 146 165 147 166 print ( f'\nConfig file readed : {IniFile} ' ) … … 149 168 ## 150 169 ## Reading prec 151 if wu.unDefined ( 'readPrec' ):170 if not readPrec : 152 171 readPrec = np.float64 153 172 else : … … 155 174 if readPrec in [ "float32", "r4", "single", "<class 'numpy.float32'>" ] : readPrec = np.float32 156 175 if readPrec in [ "float16", "r2", "half" , "<class 'numpy.float16'>" ] : readPrec = np.float16 157 176 158 177 ## Some physical constants 159 178 ## ======================= 160 if wu.unDefined ( 'Ra' ) : Ra = wu.Ra#-- Earth Radius (m)161 if wu.unDefined ( 'Grav' ) : Grav = wu.Grav#-- Gravity (m^2/s162 if wu.unDefined ( 'ICE_rho_ice' ) : ICE_rho_ice = wu.ICE_rho_ice#-- Ice volumic mass (kg/m3) in LIM3163 if wu.unDefined ( 'ICE_rho_sno') : ICE_rho_sno = wu.ICE_rho_sno#-- Snow volumic mass (kg/m3) in LIM3164 if wu.unDefined ( 'OCE_rho_liq' ) : OCE_rho_liq = wu.OCE_rho_liq#-- Ocean water volumic mass (kg/m3) in NEMO165 if wu.unDefined ( 'ATM_rho' ) : ATM_rho = wu.ATM_rho#-- Water volumic mass in atmosphere (kg/m^3)166 if wu.unDefined ( 'SRF_rho' ) : SRF_rho = wu.SRF_rho#-- Water volumic mass in surface reservoir (kg/m^3)167 if wu.unDefined ( 'RUN_rho' ) : RUN_rho = wu.RUN_rho#-- Water volumic mass of rivers (kg/m^3)168 if wu.unDefined ( 'ICE_rho_pnd' ) : ICE_rho_pnd = wu.ICE_rho_pnd#-- Water volumic mass in ice ponds (kg/m^3)169 if wu.unDefined ( 'YearLength' ) : YearLength = wu.YearLength#-- Year length (s)179 if not RA : RA = wu.RA #-- Earth Radius (m) 180 if not GRAV : GRAV = wu.GRAV #-- Gravity (m^2/s 181 if not ICE_RHO_ICE : ICE_RHO_ICE = wu.ICE_RHO_ICE #-- Ice volumic mass (kg/m3) in LIM3 182 if not ICE_RHO_SNO : ICE_RHO_SNO = wu.ICE_RHO_SNO #-- Snow volumic mass (kg/m3) in LIM3 183 if not OCE_RHO_LIQ : OCE_RHO_LIQ = wu.OCE_RHO_LIQ #-- Ocean water volumic mass (kg/m3) in NEMO 184 if not ATM_RHO : ATM_RHO = wu.ATM_RHO #-- Water volumic mass in atmosphere (kg/m^3) 185 if not SRF_RHO : SRF_RHO = wu.SRF_RHO #-- Water volumic mass in surface reservoir (kg/m^3) 186 if not RUN_RHO : RUN_RHO = wu.RUN_RHO #-- Water volumic mass of rivers (kg/m^3) 187 if not ICE_RHO_PND : ICE_RHO_PND = wu.ICE_RHO_PND #-- Water volumic mass in ice ponds (kg/m^3) 188 if not YEAR_LENGTH : YEAR_LENGTH = wu.YEAR_LENGTH #-- Year length (s) 170 189 171 190 ## Set libIGCM and machine dependant values 172 191 ## ---------------------------------------- 173 if not 'Files' in config.keys () : config['Files'] = {} 174 175 config['Physics'] = { 'Ra':str(Ra), 'Grav':str(Grav), 'ICE_rho_ice':str(ICE_rho_ice), 'ICE_rho_sno':str(ICE_rho_sno), 176 'OCE_rho_liq':str(OCE_rho_liq), 'ATM_rho':str(ATM_rho), 'SRF_rho':str(SRF_rho), 'RUN_rho':str(RUN_rho)} 177 178 config['Config'] = { 'ContinueOnError':ContinueOnError, 'TestInterp':str(TestInterp), 'readPrec':str(readPrec) } 192 if 'Files' not in config.keys () : config['Files'] = {} 193 if 'Physics' not in config.keys () : config['Physics'] = {} 194 if 'Config' not in config.keys () : config['Physics'] = {} 195 196 config['Physics'].update ( { 'RA':str(RA), 'GRAV':str(GRAV), 'ICE_RHO_ICE':str(ICE_RHO_ICE), 'ICE_RHO_SNO':str(ICE_RHO_SNO), 197 'OCE_RHO_LIQ':str(OCE_RHO_LIQ), 'ATM_RHO':str(ATM_RHO), 'SRF_RHO':str(SRF_RHO), 'RUN_RHO':str(RUN_RHO)} ) 198 199 config['Config'].update ( { 'ContinueOnError':str(ContinueOnError), 'TestInterp':str(TestInterp), 'readPrec':str(readPrec), 200 'Debug':str(Debug), 'Timer':str(Timer)} ) 201 202 Timer = wu.functools.partial (wu.Timer, debug=False, timer=True) 203 179 204 180 205 ## -------------------------- 181 ICO = ( 'ICO' in wu.ATM )182 LMDZ = ( 'LMD' in wu.ATM )206 ICO = ( 'ICO' in ATM ) 207 LMDZ = ( 'LMD' in ATM ) 183 208 184 209 mm = libIGCM_sys.config ( TagName=TagName, SpaceName=SpaceName, ExperimentName=ExperimentName, JobName=JobName, User=User, Group=Group, 185 ARCHIVE= None, SCRATCHDIR=None, STORAGE=None, R_IN=None, R_OUT=None, R_FIG=None, rebuild=None, TmpDir=None,186 R_SAVE= None, R_FIGR=None, R_BUFR=None, R_BUF_KSH=None, REBUILD_DIR=None, POST_DIR=None)210 ARCHIVE=ARCHIVE, SCRATCHDIR=SCRATCHDIR, STORAGE=STORAGE, R_IN=R_IN, R_OUT=R_OUT, R_FIG=R_FIG, rebuild=rebuild, TmpDir=TmpDir, 211 R_SAVE=R_SAVE, R_FIGR=R_FIGR, R_BUFR=R_BUFR, R_BUF_KSH=R_BUF_KSH, REBUILD_DIR=REBUILD_DIR, POST_DIR=POST_DIR, L_EXP=L_EXP ) 187 212 globals().update(mm) 188 213 189 214 config['Files']['TmpDir'] = TmpDir 190 config['libIGCM'] = { 'ARCHIVE':ARCHIVE, 'STORAGE':STORAGE, 'TmpDir':TmpDir, 'R_IN':R_IN, 'rebuild':rebuild } 215 216 if 'libIGCM' not in config.keys () : config['libIGCM'] = {} 217 config['libIGCM'].update ( { 'ARCHIVE':str(ARCHIVE), 'STORAGE':str(STORAGE), 'TmpDir':str(TmpDir), 'R_IN':str(R_IN), 'rebuild':str(rebuild) } ) 218 219 ## Debuging and timer 220 Timer = wu.functools.partial (wu.Timer, debug=Debug, timer=Timer) 191 221 192 222 ## Defines begining and end of experiment 193 223 ## -------------------------------------- 194 if wu.unDefined ( 'DateBegin' ):224 if not DateBegin : 195 225 DateBegin = f'{YearBegin}0101' 196 226 config['Experiment']['DateBegin'] = DateBegin … … 200 230 config['Experiment']['YearBegin'] = str(YearBegin) 201 231 202 if wu.unDefined ( 'DateEnd' ):232 if not DateEnd : 203 233 DateEnd = f'{YearEnd}1231' 204 234 config['Experiment']['DateEnd'] = str(DateEnd) … … 208 238 config['Experiment']['DateEnd'] = str(DateEnd) 209 239 210 if wu.unDefined ( 'PackFrequency' ):240 if not PackFrequency : 211 241 PackFrequency = YearEnd - YearBegin +1 212 242 config['Experiment']['PackFrequency'] = f'{PackFrequency}' 213 243 214 if type ( PackFrequency ) == str :215 if 'Y' in PackFrequency : PackFrequency = PackFrequency.replace ( 'Y', '') 244 if isinstance ( PackFrequency, str) : 245 if 'Y' in PackFrequency : PackFrequency = PackFrequency.replace ( 'Y', '') 216 246 if 'M' in PackFrequency : PackFrequency = PackFrequency.replace ( 'M', '') 217 247 PackFrequency = int ( PackFrequency ) … … 220 250 print ( f'{YearEnd =} {DateEnd =}' ) 221 251 print ( f'{PackFrequency=}' ) 222 252 223 253 ## Output file with water budget diagnostics 224 254 ## ----------------------------------------- 225 if wu.unDefined ( 'FileOut' ):255 if not FileOut : 226 256 FileOut = f'OCE_waterbudget_{JobName}_{YearBegin}_{YearEnd}' 227 257 if readPrec == np.float32 : FileOut = f'{FileOut}_float32' … … 230 260 config['Files']['FileOut'] = FileOut 231 261 232 f_out = open ( FileOut, mode = 'w' )262 f_out = open ( FileOut, mode = 'w', encoding='utf-8' ) 233 263 234 264 ## Useful functions 235 265 ## ---------------- 236 266 237 # Degrades precision 238 if readPrec == float : 239 def rprec (tab) : return tab 240 else : 241 def rprec (tab) : return tab.astype(readPrec).astype(float) 242 243 def kg2Sv (val, rho=ATM_rho) : 267 # Degrades precision 268 if repr(readPrec) == "<class 'numpy.float64'>" or readPrec == float : 269 def rprec (ptab) : 270 '''This version does nothing 271 272 rprec may be use to reduce floating precision when reading history files 273 ''' 274 return ptab 275 else : 276 def rprec (ptab) : 277 '''Return float with a different precision''' 278 return ptab.astype(readPrec).astype(float) 279 280 def kg2Sv (val, rho=ATM_RHO) : 244 281 '''From kg to Sverdrup''' 245 282 return val/dtime_sec*1.0e-6/rho 246 283 247 def kg2myear (val, rho=ATM_ rho) :284 def kg2myear (val, rho=ATM_RHO) : 248 285 '''From kg to m/year''' 249 286 return val/OCE_aire_tot/rho/NbYear 250 287 251 def var2prt (var, small=False, rho=ATM_rho) : 288 def var2prt (var, small=False, rho=ATM_RHO) : 289 '''Formats value for printing''' 252 290 if small : return var , kg2Sv(var, rho=rho)*1000., kg2myear(var, rho=rho)*1000. 253 291 else : return var , kg2Sv(var, rho=rho) , kg2myear(var, rho=rho) 254 292 255 def prtFlux (Desc, var, Form='F', small=False, rho=ATM_rho, width=15) : 293 def prtFlux (Desc, var, Form='F', small=False, rho=ATM_RHO, width=15) : 294 '''Pretty print of formattd value''' 256 295 if small : 257 296 if Form in ['f', 'F'] : ff=" {:14.6e} kg | {:12.4f} mSv | {:12.4f} mm/year " … … 261 300 if Form in ['e', 'E'] : ff=" {:14.6e} kg | {:12.4e} Sv | {:12.4e} m/year " 262 301 echo ( (' {:>{width}} = ' +ff).format (Desc, *var2prt(var, small, rho=rho), width=width ) ) 263 return None264 302 265 303 def echo (string, end='\n') : … … 269 307 f_out.write ( str(string) + end ) 270 308 f_out.flush () 271 return None 272 273 309 310 274 311 ## Set libIGCM directories 275 312 ## ----------------------- 276 if wu.unDefined ('R_OUT' ) : R_OUT = os.path.join ( ARCHIVE , 'IGCM_OUT' ) 277 if wu.unDefined ('R_BUF' ) : R_BUF = os.path.join ( SCRATCHDIR, 'IGCM_OUT' ) 278 if wu.unDefined ('L_EXP' ) : L_EXP = os.path.join (TagName, SpaceName, ExperimentName, JobName) 279 if wu.unDefined ('R_SAVE' ) : R_SAVE = os.path.join ( R_OUT, L_EXP ) 280 if wu.unDefined ('R_BUFR' ) : R_BUFR = os.path.join ( R_BUF, L_EXP ) 281 if wu.unDefined ('POST_DIR' ) : POST_DIR = os.path.join ( R_BUFR, 'Out' ) 282 if wu.unDefined ('REBUILD_DIR') : REBUILD_DIR = os.path.join ( R_BUFR, 'REBUILD' ) 283 if wu.unDefined ('R_BUF_KSH' ) : R_BUF_KSH = os.path.join ( R_BUFR, 'Out' ) 284 if wu.unDefined ('R_FIGR' ) : R_FIGR = os.path.join ( STORAGE, 'IGCM_OUT', L_EXP ) 285 286 config['libIGCM'].update ( { 'R_OUT':R_OUT, 'R_BUF':R_BUF, 'L_EXP':L_EXP, 'R_BUFR':R_BUFR, 'POST_DIR':POST_DIR, 287 'REBUILD_DIR':REBUILD_DIR, 'R_BUF_KSH':R_BUF_KSH, 'R_FIGR':R_FIGR, 'rebuild':rebuild } ) 313 if not R_OUT : R_OUT = os.path.join ( ARCHIVE , 'IGCM_OUT' ) 314 if not R_BUF : R_BUF = os.path.join ( SCRATCHDIR, 'IGCM_OUT' ) 315 if not L_EXP : L_EXP = os.path.join (TagName, SpaceName, ExperimentName, JobName) 316 if not R_SAVE : R_SAVE = os.path.join ( R_OUT, L_EXP ) 317 if not R_BUFR : R_BUFR = os.path.join ( R_BUF, L_EXP ) 318 if not POST_DIR : POST_DIR = os.path.join ( R_BUFR, 'Out' ) 319 if not REBUILD_DIR : REBUILD_DIR = os.path.join ( R_BUFR, 'REBUILD' ) 320 if not R_BUF_KSH : R_BUF_KSH = os.path.join ( R_BUFR, 'Out' ) 321 if not R_FIGR : R_FIGR = os.path.join ( STORAGE, 'IGCM_OUT', L_EXP ) 322 323 config['libIGCM'].update ( { 'R_OUT':R_OUT, 'R_BUF':R_BUF, 'L_EXP':L_EXP, 'R_BUFR':R_BUFR, 'R_SAVE':R_SAVE, 'POST_DIR':POST_DIR, 324 'REBUILD_DIR':REBUILD_DIR, 'R_BUF_KSH':R_BUF_KSH, 'R_FIGR':R_FIGR, 'rebuild':rebuild, 325 'YEAR_LENGTH':str(YEAR_LENGTH)} ) 288 326 289 327 ## Set directory to extract files 290 328 ## ------------------------------ 291 if wu.unDefined ( 'FileDir' ): FileDir = os.path.join ( TmpDir, f'WATER_{JobName}' )329 if not FileDir : FileDir = os.path.join ( TmpDir, f'WATER_{JobName}' ) 292 330 config['Files']['FileDir'] = FileDir 293 331 … … 295 333 296 334 ##- Set directories to rebuild ocean and ice restart files 297 if wu.unDefined ( 'FileDirOCE' ): FileDirOCE = os.path.join ( FileDir, 'OCE' )298 if wu.unDefined ( 'FileDirICE' ): FileDirICE = os.path.join ( FileDir, 'ICE' )335 if not FileDirOCE : FileDirOCE = os.path.join ( FileDir, 'OCE' ) 336 if not FileDirICE : FileDirICE = os.path.join ( FileDir, 'ICE' ) 299 337 if not os.path.exists ( FileDirOCE ) : os.mkdir ( FileDirOCE ) 300 338 if not os.path.exists ( FileDirICE ) : os.mkdir ( FileDirICE ) … … 314 352 if Freq == "MO" : FreqDir = os.path.join ( 'Output' , 'MO' ) 315 353 if Freq == "SE" : FreqDir = os.path.join ( 'Analyse', 'SE' ) 316 if wu.unDefined ( 'dir_OCE_his' ):354 if not dir_OCE_his : 317 355 dir_OCE_his = os.path.join ( R_SAVE, "OCE", FreqDir ) 318 356 config['Files']['dir_OCE_his'] = dir_OCE_his 319 if wu.unDefined ( 'dir_ICE_his' ):357 if not dir_ICE_his : 320 358 dir_ICE_his = os.path.join ( R_SAVE, "ICE", FreqDir ) 321 359 config['Files']['dir_OCE_his'] = dir_OCE_his 322 323 echo ( f'The analysis relies on files from the following model output directories : ' )360 361 echo ( 'The analysis relies on files from the following model output directories : ' ) 324 362 echo ( f'{dir_OCE_his}' ) 325 363 echo ( f'{dir_ICE_his}' ) 326 364 327 365 #-- Creates files names 328 if wu.unDefined ( 'Period ' ):366 if not Period : 329 367 if Freq == 'MO' : Period = f'{DateBegin}_{DateEnd}_1M' 330 368 if Freq == 'SE' : Period = f'SE_{DateBegin}_{DateEnd}_1M' … … 333 371 config['Files']['DateBegin'] = DateBegin 334 372 config['Files']['DateBegin'] = DateEnd 335 373 336 374 echo ( f'Period : {Period}' ) 337 375 338 if wu.unDefined ( 'FileCommon' ):376 if not FileCommon : 339 377 FileCommon = f'{JobName}_{Period}' 340 378 config['Files']['FileCommon'] = FileCommon 341 379 342 if wu.unDefined ( 'Title' ):380 if not Title : 343 381 Title = f'{JobName} : {Freq} : {DateBegin} - {DateEnd}' 344 382 config['Files']['Title'] = Title 345 383 346 384 echo ('\nOpen history files' ) 347 if wu.unDefined ('file_OCE_his' ):385 if not file_OCE_his : 348 386 file_OCE_his = os.path.join ( dir_OCE_his, f'{FileCommon}_grid_T.nc' ) 349 file_OCE_his= file_OCE_his350 if wu.unDefined ('file_OCE_sca' ) :387 config['Files']['file_OCE_his'] = file_OCE_his 388 if not file_OCE_sca : 351 389 file_OCE_sca = os.path.join ( dir_OCE_his, f'{FileCommon}_scalar.nc' ) 352 390 config['Files']['file_OCE_sca'] = file_OCE_sca 353 if wu.unDefined ( 'file_ICE_hi' ) :391 if not file_ICE_his : 354 392 file_ICE_his = os.path.join ( dir_ICE_his, f'{FileCommon}_icemod.nc' ) 355 393 config['Files']['file_ICE_his'] = file_ICE_his … … 358 396 d_OCE_sca = xr.open_dataset ( file_OCE_sca, use_cftime=True, decode_times=True, decode_cf=True ).squeeze() 359 397 d_ICE_his = xr.open_dataset ( file_ICE_his, use_cftime=True, decode_times=True, decode_cf=True ).squeeze() 360 if NEMO == 3.6 :d_ICE_his = d_ICE_his.rename ( {'y_grid_T':'y', 'x_grid_T':'x'} ) 398 399 udims = udims={'y':'y', 'x':'x'} 400 d_OCE_his = nemo.unify_dims ( d_OCE_his, **udims, verbose=False ) 401 d_OCE_sca = nemo.unify_dims ( d_OCE_sca, **udims, verbose=False ) 402 d_ICE_his = nemo.unify_dims ( d_ICE_his, **udims, verbose=False ) 361 403 362 404 echo ( f'{file_OCE_his = }' ) … … 367 409 ## ------------------ 368 410 dtime = ( d_OCE_his.time_counter_bounds.max() - d_OCE_his.time_counter_bounds.min() ) 369 echo ( f'\nRun length : {(dtime/np.timedelta64(1, "D")).values:8.2f} days' )411 echo ( '\nRun length : {(dtime/np.timedelta64(1, "D")).values:8.2f} days' ) 370 412 dtime_sec = (dtime/np.timedelta64(1, "s")).values.item() # Convert in seconds 371 413 … … 373 415 ## ----------------------------- 374 416 dtime_per = ( d_OCE_his.time_counter_bounds[:,-1] - d_OCE_his.time_counter_bounds[:,0] ) 375 echo ( f'\nPeriods lengths (days) : ')417 echo ( '\nPeriods lengths (days) : ') 376 418 echo ( f' {(dtime_per/np.timedelta64(1, "D")).values}' ) 377 419 dtime_per_sec = (dtime_per/np.timedelta64(1, "s")).values # In seconds … … 380 422 381 423 ## Number of years 382 NbYear = dtime_sec / Y earLength424 NbYear = dtime_sec / YEAR_LENGTH 383 425 384 426 ##-- Extract restart files from tar … … 387 429 if wu.unDefined ('TarRestartDate_end' ) : TarRestartDate_end = wu.FormatToGregorian ( DateEnd ) 388 430 389 if wu.unDefined ( 'TarRestartPeriod_beg' ):431 if not TarRestartPeriod_beg : 390 432 391 433 TarRestartPeriod_beg_DateEnd = TarRestartDate_beg 392 434 TarRestartPeriod_beg_DateBeg = wu.DateAddYear ( TarRestartPeriod_beg_DateEnd, -PackFrequency ) 393 435 TarRestartPeriod_beg_DateBeg = wu.DatePlusOneDay ( TarRestartPeriod_beg_DateBeg ) 394 436 395 437 TarRestartPeriod_beg = f'{TarRestartPeriod_beg_DateBeg}_{TarRestartPeriod_beg_DateEnd}' 396 438 echo (f'Tar period for initial restart : {TarRestartPeriod_beg}') 397 439 config['Files']['TarRestartPeriod_beg'] = TarRestartPeriod_beg 398 440 399 if wu.unDefined ( 'TarRestartPeriod_end' ):441 if not TarRestartPeriod_end : 400 442 401 443 TarRestartPeriod_end_DateEnd = TarRestartDate_end 402 444 TarRestartPeriod_end_DateBeg = wu.DateAddYear ( TarRestartPeriod_end_DateEnd, -PackFrequency ) 403 445 TarRestartPeriod_end_DateBeg = wu.DatePlusOneDay ( TarRestartPeriod_end_DateBeg ) 404 446 405 447 TarRestartPeriod_end = f'{TarRestartPeriod_end_DateBeg}_{TarRestartPeriod_end_DateEnd}' 406 448 echo (f'Tar period for final restart : {TarRestartPeriod_end}') … … 409 451 echo (f'Restart dates - Start : {TarRestartPeriod_beg} / End : {TarRestartPeriod_end}') 410 452 411 if wu.unDefined ( 'tar_restart_beg' ):453 if not tar_restart_beg : 412 454 tar_restart_beg = os.path.join ( R_SAVE, 'RESTART', f'{JobName}_{TarRestartPeriod_beg}_restart.tar' ) 413 455 config['Files']['tar_restart_beg'] = tar_restart_beg 414 if wu.unDefined ( 'tar_restart_end' ):456 if not tar_restart_end : 415 457 tar_restart_end = os.path.join ( R_SAVE, 'RESTART', f'{JobName}_{TarRestartPeriod_end}_restart.tar' ) 416 458 config['Files']['tar_restart_end'] = tar_restart_end … … 418 460 echo ( f'{tar_restart_beg = }' ) 419 461 echo ( f'{tar_restart_end = }' ) 420 462 421 463 # 422 if wu.unDefined ('tar_restart_beg_ATM' ) : tar_restart_beg_ATM = tar_restart_beg 423 if wu.unDefined ('tar_restart_beg_DYN' ) : tar_restart_beg_DYN = tar_restart_beg 424 if wu.unDefined ('tar_restart_beg_SRF' ) : tar_restart_beg_SRF = tar_restart_beg 425 if wu.unDefined ('tar_restart_beg_RUN' ) : tar_restart_beg_RUN = tar_restart_beg 426 if wu.unDefined ('tar_restart_beg_OCE' ) : tar_restart_beg_OCE = tar_restart_beg 427 if wu.unDefined ('tar_restart_beg_ICE' ) : tar_restart_beg_ICE = tar_restart_beg 428 429 if wu.unDefined ('tar_restart_end_ATM' ) : tar_restart_end_ATM = tar_restart_end 430 if wu.unDefined ('tar_restart_end_DYN' ) : tar_restart_end_DYN = tar_restart_end 431 if wu.unDefined ('tar_restart_end_SRF' ) : tar_restart_end_SRF = tar_restart_end 432 if wu.unDefined ('tar_restart_end_RUN' ) : tar_restart_end_RUN = tar_restart_end 433 if wu.unDefined ('tar_restart_end_OCE' ) : tar_restart_end_OCE = tar_restart_end 434 if wu.unDefined ('tar_restart_end_ICE' ) : tar_restart_end_ICE = tar_restart_end 435 436 if wu.unDefined ( 'file_OCE_beg' ) : 464 if not tar_restart_beg_ATM : tar_restart_beg_ATM = tar_restart_beg 465 if not tar_restart_beg_DYN : tar_restart_beg_DYN = tar_restart_beg 466 if not tar_restart_beg_RUN : tar_restart_beg_RUN = tar_restart_beg 467 if not tar_restart_beg_OCE : tar_restart_beg_OCE = tar_restart_beg 468 if not tar_restart_beg_ICE : tar_restart_beg_ICE = tar_restart_beg 469 470 if not tar_restart_end_ATM : tar_restart_end_ATM = tar_restart_end 471 if not tar_restart_end_DYN : tar_restart_end_DYN = tar_restart_end 472 if not tar_restart_end_RUN : tar_restart_end_RUN = tar_restart_end 473 if not tar_restart_end_OCE : tar_restart_end_OCE = tar_restart_end 474 if not tar_restart_end_ICE : tar_restart_end_ICE = tar_restart_end 475 476 if not file_OCE_beg : 437 477 file_OCE_beg = f'{FileDir}/OCE_{JobName}_{TarRestartDate_beg}_restart.nc' 438 478 config['Files']['file_OCE_beg'] = file_OCE_beg 439 if wu.unDefined ( 'file_OCE_end' ):479 if not file_OCE_end : 440 480 file_OCE_end = f'{FileDir}/OCE_{JobName}_{TarRestartDate_end}_restart.nc' 441 481 config['Files']['file_OCE_end'] = file_OCE_end 442 if wu.unDefined ( 'file_ICE_beg' ):482 if not file_ICE_beg : 443 483 file_ICE_beg = f'{FileDir}/ICE_{JobName}_{TarRestartDate_beg}_restart_icemod.nc' 444 484 config['Files']['file_ICE_beg'] = file_ICE_beg 445 if wu.unDefined ( 'file_ICE_end' ) :485 if not file_ICE_end : 446 486 file_ICE_end = f'{FileDir}/ICE_{JobName}_{TarRestartDate_end}_restart_icemod.nc' 447 487 config['Files']['file_ICE_end'] = file_ICE_end … … 463 503 return int (ndomain_opa) 464 504 465 def extract_and_rebuild ( file_name=file_OCE_beg, tar_restart=tar_restart_end, FileDirComp=FileDirOCE, ErrorCount=ErrorCount ) : 505 @Timer 506 def extract_and_rebuild ( file_name=file_OCE_beg, tar_restart=tar_restart_end, file_dir_comp=FileDirOCE, error_count=ErrorCount ) : 466 507 '''Extract restart file from tar. Rebuild ocean files if needed''' 467 echo ( f'----------')508 echo ( '----------') 468 509 if os.path.exists ( file_name ) : 469 510 echo ( f'-- File ready : {file_name = }' ) 470 else : 511 else : 471 512 echo ( f'-- Extracting {file_name = }' ) 472 513 base_resFile = Path (file_name).stem # basename, and remove suffix 473 514 # Try to extract the rebuilded file 474 515 if os.path.exists ( tar_restart ) : 475 command = f'cd { FileDirComp} ; tar xf {tar_restart} {base_resFile}.nc'516 command = f'cd {file_dir_comp} ; tar xf {tar_restart} {base_resFile}.nc' 476 517 echo ( command ) 477 try : 518 try : 478 519 os.system ( command ) 479 520 except : 480 521 if not os.path.exists ( os.path.join (FileDir, f'{base_resFile}_0000.nc') ): 481 command = f'cd { FileDirComp} ; tar xf {tar_restart_end} {base_file}_*.nc'522 command = f'cd {file_dir_comp} ; tar xf {tar_restart_end} {base_resFile}_*.nc' 482 523 echo ( command ) 483 524 ierr = os.system ( command ) 484 525 if ierr == 0 : echo ( f'tar done : {base_resFile}.nc') 485 else : raise Exception( f'command failed : {command}' )486 echo ( f'extract ndomain' )487 ndomain_opa = get_ndomain ( os.path.join (FileDir, f'{base_ file}') )488 command = f'cd { FileDirComp} ; {rebuild} {base_resFile} {ndomain_opa} ; mv {base_resFile}.nc {FileDir}'526 else : raise OSError ( f'command failed : {command}' ) 527 echo ( 'extract ndomain' ) 528 ndomain_opa = get_ndomain ( os.path.join (FileDir, f'{base_resFile}') ) 529 command = f'cd {file_dir_comp} ; {rebuild} {base_resFile} {ndomain_opa} ; mv {base_resFile}.nc {FileDir}' 489 530 echo ( command ) 490 531 ierr = os.system ( command ) 491 532 if ierr == 0 : echo ( f'Rebuild done : {base_resFile}.nc') 492 else : raise Exception( f'command failed : {command}' )493 else : 533 else : raise OSError ( f'command failed : {command}' ) 534 else : 494 535 echo ( f'tar done : {base_resFile}') 495 command = f'cd { FileDirComp} ; mv {base_resFile}.nc {FileDir}'536 command = f'cd {file_dir_comp} ; mv {base_resFile}.nc {FileDir}' 496 537 ierr = os.system ( command ) 497 538 if ierr == 0 : echo ( f'command done : {command}' ) 498 else : raise Exception ( f'command failed : {command = }' )539 else : raise OSError ( f'command failed : {command = }' ) 499 540 else : 500 541 echo ( f'****** Tar restart file {tar_restart = } not found ' ) 501 542 if ContinueOnError : 502 ErrorCount += 1 503 else : 504 raise Exception ( f'****** tar file not found {tar_restart = } - Stopping' ) 505 return ErrorCount 506 507 508 ErrorCount += extract_and_rebuild ( file_name=file_OCE_beg, tar_restart=tar_restart_beg, FileDirComp=FileDirOCE ) 509 ErrorCount += extract_and_rebuild ( file_name=file_OCE_end, tar_restart=tar_restart_end, FileDirComp=FileDirOCE ) 510 ErrorCount += extract_and_rebuild ( file_name=file_ICE_beg, tar_restart=tar_restart_beg, FileDirComp=FileDirICE ) 511 ErrorCount += extract_and_rebuild ( file_name=file_ICE_end, tar_restart=tar_restart_end, FileDirComp=FileDirICE ) 543 error_count += 1 544 else : 545 raise OSError ( f'****** tar file not found {tar_restart = } - Stopping' ) 546 return error_count 547 548 ErrorCount += extract_and_rebuild ( file_name=file_OCE_beg, tar_restart=tar_restart_beg, file_dir_comp=FileDirOCE, error_count=ErrorCount ) 549 ErrorCount += extract_and_rebuild ( file_name=file_OCE_end, tar_restart=tar_restart_end, file_dir_comp=FileDirOCE, error_count=ErrorCount ) 550 ErrorCount += extract_and_rebuild ( file_name=file_ICE_beg, tar_restart=tar_restart_beg, file_dir_comp=FileDirICE, error_count=ErrorCount ) 551 ErrorCount += extract_and_rebuild ( file_name=file_ICE_end, tar_restart=tar_restart_end, file_dir_comp=FileDirICE, error_count=ErrorCount ) 512 552 513 553 echo ('Opening OCE and ICE restart files') 514 if NEMO == 3.6 : 554 if NEMO == 3.6 : 515 555 d_OCE_beg = xr.open_dataset ( os.path.join (FileDir, file_OCE_beg), decode_times=False, decode_cf=True, drop_variables=['y', 'x']).squeeze() 516 556 d_OCE_end = xr.open_dataset ( os.path.join (FileDir, file_OCE_end), decode_times=False, decode_cf=True).squeeze() 517 557 d_ICE_beg = xr.open_dataset ( os.path.join (FileDir, file_ICE_beg), decode_times=False, decode_cf=True).squeeze() 518 558 d_ICE_end = xr.open_dataset ( os.path.join (FileDir, file_ICE_end), decode_times=False, decode_cf=True).squeeze() 519 if NEMO == 4.0 or NEMO == 4.2 :559 if NEMO in [ 4.0, 4.2 ] : 520 560 d_OCE_beg = xr.open_dataset ( os.path.join (FileDir, file_OCE_beg), decode_times=False, decode_cf=True, drop_variables=['y', 'x']).squeeze() 521 561 d_OCE_end = xr.open_dataset ( os.path.join (FileDir, file_OCE_end), decode_times=False, decode_cf=True, drop_variables=['y', 'x']).squeeze() … … 524 564 525 565 ## Write the full configuration 526 config_out = open (FullIniFile, 'w' )566 config_out = open (FullIniFile, 'w', encoding='utf-8') 527 567 config.write (config_out ) 528 568 config_out.close () 529 569 530 570 # Get mask and surfaces 531 571 sos = d_OCE_his ['sos'][0].squeeze() … … 539 579 ICE_aire = OCE_aire 540 580 581 @Timer 541 582 def OCE_stock_int (stock) : 542 583 '''Integrate stock on ocean grid''' 543 OCE_stock_int =wu.Psum ( (stock * OCE_aire ).to_masked_array().ravel() )544 return OCE_stock_int 545 584 return wu.Psum ( (stock * OCE_aire ).to_masked_array().ravel() ) 585 586 @Timer 546 587 def ONE_stock_int (stock) : 547 588 '''Sum stock''' 548 ONE_stock_int =wu.Psum ( stock.to_masked_array().ravel() )549 return ONE_stock_int 550 589 return wu.Psum ( stock.to_masked_array().ravel() ) 590 591 @Timer 551 592 def OCE_flux_int (flux) : 552 593 '''Integrate flux on oce grid''' 553 OCE_flux_int =wu.Psum ( (flux * OCE_aire * dtime_per_sec).to_masked_array().ravel() )554 return OCE_flux_int 555 594 return wu.Psum ( (flux * OCE_aire * dtime_per_sec).to_masked_array().ravel() ) 595 596 @Timer 556 597 def ONE_flux_int (flux) : 557 598 '''Integrate flux on oce grid''' 558 OCE_flux_int = wu.Psum ( (flux * dtime_per_sec).to_masked_array().ravel() ) 559 return OCE_flux_int 599 return wu.Psum ( (flux * dtime_per_sec).to_masked_array().ravel() ) 560 600 561 601 OCE_aire_tot = ONE_stock_int ( OCE_aire ) 562 602 ICE_aire_tot = ONE_stock_int ( ICE_aire ) 563 603 564 604 echo ( '\n------------------------------------------------------------------------------------' ) 565 605 echo ( '-- NEMO change in stores (for the records)' ) … … 582 622 583 623 echo ( f'OCE_sum_ssh_beg = {OCE_sum_ssh_beg:12.6e} m^3 - OCE_sum_ssh_end = {OCE_sum_ssh_end:12.6e} m^3' ) 584 dOCE_ssh_vol = ( OCE_sum_ssh_end - OCE_sum_ssh_beg )585 dOCE_ssh_mas = dOCE_ssh_vol * OCE_ rho_liq624 dOCE_ssh_vol = OCE_sum_ssh_end - OCE_sum_ssh_beg 625 dOCE_ssh_mas = dOCE_ssh_vol * OCE_RHO_LIQ 586 626 587 627 if NEMO == 3.6 : 588 628 echo ( f'OCE_sum_e3tn_beg = {OCE_sum_e3tn_beg:12.6e} m^3 - OCE_sum_e3tn_end = {OCE_sum_e3tn_end:12.6e} m^3' ) 589 dOCE_e3tn_vol = ( OCE_sum_e3tn_end - OCE_sum_e3tn_beg )590 dOCE_e3tn_mas = dOCE_e3tn_vol * OCE_ rho_liq629 dOCE_e3tn_vol = OCE_sum_e3tn_end - OCE_sum_e3tn_beg 630 dOCE_e3tn_mas = dOCE_e3tn_vol * OCE_RHO_LIQ 591 631 592 632 dOCE_vol_wat = dOCE_ssh_vol ; dOCE_mas_wat = dOCE_ssh_mas … … 601 641 602 642 ## Glace et neige 603 if NEMO == 3.6 : 643 if NEMO == 3.6 : 604 644 ICE_ice_beg = d_ICE_beg['v_i_htc1']+d_ICE_beg['v_i_htc2']+d_ICE_beg['v_i_htc3']+d_ICE_beg['v_i_htc4']+d_ICE_beg['v_i_htc5'] 605 645 ICE_ice_end = d_ICE_end['v_i_htc1']+d_ICE_end['v_i_htc2']+d_ICE_end['v_i_htc3']+d_ICE_end['v_i_htc4']+d_ICE_end['v_i_htc5'] … … 607 647 ICE_sno_beg = d_ICE_beg['v_s_htc1']+d_ICE_beg['v_s_htc2']+d_ICE_beg['v_s_htc3']+d_ICE_beg['v_s_htc4']+d_ICE_beg['v_s_htc5'] 608 648 ICE_sno_end = d_ICE_end['v_s_htc1']+d_ICE_end['v_s_htc2']+d_ICE_end['v_s_htc3']+d_ICE_end['v_s_htc4']+d_ICE_end['v_s_htc5'] 609 649 610 650 ICE_pnd_beg = 0.0 ; ICE_pnd_end = 0.0 ; ICE_fzl_beg = 0.0 ; ICE_fzl_end = 0.0 611 651 612 ICE_mas_wat_beg = ICE_ice_beg*ICE_ rho_ice + ICE_sno_beg*ICE_rho_sno613 ICE_mas_wat_end = ICE_ice_end*ICE_ rho_ice + ICE_sno_end*ICE_rho_sno614 615 if NEMO == 4.0 or NEMO == 4.2:652 ICE_mas_wat_beg = ICE_ice_beg*ICE_RHO_ICE + ICE_sno_beg*ICE_RHO_SNO 653 ICE_mas_wat_end = ICE_ice_end*ICE_RHO_ICE + ICE_sno_end*ICE_RHO_SNO 654 655 if NEMO in [ 4.0, 4.2 ] : 616 656 ICE_ice_beg = d_ICE_beg['v_i'] ; ICE_ice_end = d_ICE_end['v_i'] 617 ICE_sno_beg = d_ICE_beg['v_s'] ; ICE_sno_end = d_ICE_end['v_s'] 657 ICE_sno_beg = d_ICE_beg['v_s'] ; ICE_sno_end = d_ICE_end['v_s'] 618 658 ICE_pnd_beg = d_ICE_beg['v_ip'] ; ICE_pnd_end = d_ICE_end['v_ip'] # Ice ponds 619 659 ICE_fzl_beg = d_ICE_beg['v_il'] ; ICE_fzl_end = d_ICE_end['v_il'] # Frozen Ice Ponds 620 660 621 661 ICE_mas_wat_beg = OCE_stock_int ( d_ICE_beg['snwice_mass'] ) 622 662 ICE_mas_wat_end = OCE_stock_int ( d_ICE_end['snwice_mass'] ) 623 663 624 664 ICE_vol_ice_beg = OCE_stock_int ( ICE_ice_beg ) 625 665 ICE_vol_ice_end = OCE_stock_int ( ICE_ice_end ) … … 636 676 #-- Converting to freswater volume 637 677 dICE_vol_ice = ICE_vol_ice_end - ICE_vol_ice_beg 638 dICE_mas_ice = dICE_vol_ice * ICE_ rho_ice678 dICE_mas_ice = dICE_vol_ice * ICE_RHO_ICE 639 679 640 680 dICE_vol_sno = ICE_vol_sno_end - ICE_vol_sno_beg 641 dICE_mas_sno = dICE_vol_sno * ICE_ rho_sno681 dICE_mas_sno = dICE_vol_sno * ICE_RHO_SNO 642 682 643 683 dICE_vol_pnd = ICE_vol_pnd_end - ICE_vol_pnd_beg 644 dICE_mas_pnd = dICE_vol_pnd * ICE_ rho_pnd684 dICE_mas_pnd = dICE_vol_pnd * ICE_RHO_PND 645 685 646 686 dICE_vol_fzl= ICE_vol_fzl_end - ICE_vol_fzl_beg 647 dICE_mas_fzl = dICE_vol_fzl * ICE_ rho_pnd687 dICE_mas_fzl = dICE_vol_fzl * ICE_RHO_PND 648 688 649 689 if NEMO == 3.6 : 650 dICE_mas_wat = dICE_mas_ice + dICE_mas_sno 690 dICE_mas_wat = dICE_mas_ice + dICE_mas_sno 651 691 dSEA_mas_wat = dOCE_mas_wat + dICE_mas_ice + dICE_mas_sno 652 692 653 if NEMO == 4.0 or NEMO == 4.2:693 if NEMO in [4.0, 4.2 ] : 654 694 dICE_mas_wat = ICE_mas_wat_end - ICE_mas_wat_beg 655 695 dSEA_mas_wat = dOCE_mas_wat + dICE_mas_wat … … 664 704 echo ( f'dICE_vol_pnd = {dICE_vol_pnd:12.3e} m^3' ) 665 705 echo ( f'dICE_mas_ice = {dICE_mas_ice:12.3e} m^3' ) 666 echo ( f'dICE_mas_sno = {dICE_mas_sno:12.3e} m^3' ) 667 echo ( f'dICE_mas_pnd = {dICE_mas_pnd:12.3e} m^3' ) 668 echo ( f'dICE_mas_fzl = {dICE_mas_fzl:12.3e} m^3' ) 706 echo ( f'dICE_mas_sno = {dICE_mas_sno:12.3e} m^3' ) 707 echo ( f'dICE_mas_pnd = {dICE_mas_pnd:12.3e} m^3' ) 708 echo ( f'dICE_mas_fzl = {dICE_mas_fzl:12.3e} m^3' ) 669 709 670 710 echo ( '\n------------------------------------------------------------') … … 704 744 OCE_friver = rprec (d_OCE_his['friver'] ) ; OCE_mas_friver = OCE_flux_int ( OCE_friver ) 705 745 OCE_runoffs = rprec (d_OCE_his['runoffs'] ) ; OCE_mas_runoffs = OCE_flux_int ( OCE_runoffs ) 706 if NEMO == 4.0 or NEMO == 4.2:746 if NEMO in [ 4.0, 4.2 ] : 707 747 OCE_wfxice = rprec (d_OCE_his['vfxice']) ; OCE_mas_wfxice = OCE_flux_int ( OCE_wfxice ) 708 748 OCE_wfxsnw = rprec (d_OCE_his['vfxsnw']) ; OCE_mas_wfxsnw = OCE_flux_int ( OCE_wfxsnw ) 709 749 OCE_wfxsub = rprec (d_OCE_his['vfxsub']) ; OCE_mas_wfxsub = OCE_flux_int ( OCE_wfxsub ) 710 750 if NEMO == 3.6 : 711 OCE_wfxice = rprec (d_OCE_his['vfxice'])/ 86400.*ICE_rho_ice; OCE_mas_wfxice = OCE_flux_int ( OCE_wfxice )712 OCE_wfxsnw = rprec (d_OCE_his['vfxsnw'])/ 86400.*ICE_rho_sno; OCE_mas_wfxsnw = OCE_flux_int ( OCE_wfxsnw )713 OCE_wfxsub = rprec (d_OCE_his['vfxsub'])/ 86400.*ICE_rho_sno; OCE_mas_wfxsub = OCE_flux_int ( OCE_wfxsub )751 OCE_wfxice = rprec (d_OCE_his['vfxice'])/nemo.RDAY*ICE_RHO_ICE ; OCE_mas_wfxice = OCE_flux_int ( OCE_wfxice ) 752 OCE_wfxsnw = rprec (d_OCE_his['vfxsnw'])/nemo.RDAY*ICE_RHO_SNO ; OCE_mas_wfxsnw = OCE_flux_int ( OCE_wfxsnw ) 753 OCE_wfxsub = rprec (d_OCE_his['vfxsub'])/nemo.RDAY*ICE_RHO_SNO ; OCE_mas_wfxsub = OCE_flux_int ( OCE_wfxsub ) 714 754 # Additional checks 715 755 OCE_evap_oce = rprec (d_OCE_his['evap_ao_cea']) ; OCE_mas_evap_oce = OCE_flux_int ( OCE_evap_oce ) … … 719 759 OCE_rain = rprec (d_OCE_his['rain'] ) ; OCE_mas_rain = OCE_flux_int ( OCE_rain ) 720 760 ICE_wfxsub_err = rprec (d_ICE_his['vfxsub_err'] ) ; ICE_mas_wfxsub_err = OCE_flux_int ( ICE_wfxsub_err ) 721 if NEMO == 4.0 or NEMO == 4.2:761 if NEMO in [ 4.0, 4.2 ] : 722 762 ICE_wfxpnd = rprec (d_ICE_his['vfxpnd'] ) ; ICE_mas_wfxpnd = OCE_flux_int ( ICE_wfxpnd ) 723 763 ICE_wfxsnw_sub = rprec (d_ICE_his['vfxsnw_sub']) ; ICE_mas_wfxsnw_sub = OCE_flux_int ( ICE_wfxsnw_sub ) … … 725 765 if NEMO == 3.6 : 726 766 ICE_wfxpnd = 0.0 ; ICE_mas_wfxpnd = 0.0 727 ICE_wfxsnw_sub = rprec (d_ICE_his['vfxsub'])/ 86400.*ICE_rho_sno; ICE_mas_wfxsnw_sub = OCE_flux_int ( ICE_wfxsnw_sub )728 ICE_wfxsnw_pre = rprec (d_ICE_his['vfxspr'])/ 86400.*ICE_rho_sno ; ICE_mas_wfxsnw_pre = OCE_flux_int ( ICE_wfxsnw_pre)767 ICE_wfxsnw_sub = rprec (d_ICE_his['vfxsub'])/nemo.RDAY*ICE_RHO_SNO ; ICE_mas_wfxsnw_sub = OCE_flux_int ( ICE_wfxsnw_sub ) 768 ICE_wfxsnw_pre = rprec (d_ICE_his['vfxspr'])/nemo.RDAY*ICE_RHO_SNO ; ICE_mas_wfxsnw_pre = OCE_flux_int ( ICE_wfxsnw_pre ) 729 769 730 770 OCE_wfcorr = rprec (d_OCE_his['wfcorr']) ; OCE_mas_wfcorr = OCE_flux_int ( OCE_wfcorr ) … … 733 773 OCE_vflx_fwb = rprec (d_OCE_his['vflx_fwb']) ; OCE_mas_vflx_fwb = OCE_flux_int ( OCE_vflx_fwb ) 734 774 OCE_vflx_ssr = rprec (d_OCE_his['vflx_ssr']) ; OCE_mas_vflx_ssr = OCE_flux_int ( OCE_vflx_ssr ) 735 else : 775 else : 736 776 OCE_fwb = 0.0 ; OCE_mas_fwb = 0.0 737 777 OCE_ssr = 0.0 ; OCE_mas_ssr = 0.0 … … 746 786 OCE_mas_all = OCE_mas_emp_oce + OCE_mas_emp_ice - OCE_mas_runoffs - OCE_mas_iceshelf 747 787 748 echo ('\n-- Fields:' ) 788 echo ('\n-- Fields:' ) 749 789 prtFlux ('OCE+ICE budget ', OCE_mas_all , 'e', True) 750 790 prtFlux (' EMPMR ', OCE_mas_empmr , 'e', True) … … 756 796 prtFlux (' ICESHELF ', OCE_mas_iceshelf , 'e', True) 757 797 prtFlux (' CALVING ', OCE_mas_calving , 'e', True) 758 prtFlux (' FRIVER ', OCE_mas_friver , 'e', True) 798 prtFlux (' FRIVER ', OCE_mas_friver , 'e', True) 759 799 prtFlux (' RUNOFFS ', OCE_mas_runoffs , 'e', True) 760 800 prtFlux (' WFXICE ', OCE_mas_wfxice , 'e', True) … … 783 823 784 824 785 echo ('\n===> Comparing ===>' ) 825 echo ('\n===> Comparing ===>' ) 786 826 echo (' WFX OCE = -empmr + iceshelf = {:12.5e} (kg) '.format ( -OCE_mas_empmr + OCE_mas_iceshelf) ) 787 827 echo (' versus d OCE = {:12.5e} (kg) '.format ( dOCE_mas_wat) ) 788 828 echo (' WFX ICE+SNW+PND 1 = emp_ice - wfxice - wfxsnw - wfxpnd - wfxsub_err = {:12.5e} (kg) '.format ( -OCE_mas_emp_ice - OCE_mas_wfxice - OCE_mas_wfxsnw - ICE_mas_wfxpnd - ICE_mas_wfxsub_err) ) 789 echo (' WFX ICE+SNW+PND 2 = -emp_ice + empmr - emp_oce + runoffs = {:12.5e} (kg) '.format ( -OCE_mas_emp_ice + OCE_mas_empmr - OCE_mas_emp_oce + OCE_mas_runoffs )) 829 echo (' WFX ICE+SNW+PND 2 = -emp_ice + empmr - emp_oce + runoffs = {:12.5e} (kg) '.format ( -OCE_mas_emp_ice + OCE_mas_empmr - OCE_mas_emp_oce + OCE_mas_runoffs )) 790 830 echo (' versus d ICE+SNW+PND = {:12.5e} (kg) '.format ( dICE_mas_wat)) # Manque PND ? 791 if Coupled : 831 if Coupled : 792 832 echo (' WFX OCE+ICE+SNW+PND = -emp_oce - emp_ice + runoffs + iceshelf = {:12.5e} (kg) '.format ( -OCE_mas_emp_oce - OCE_mas_emp_ice + OCE_mas_runoffs + OCE_mas_iceshelf)) 793 833 else : … … 796 836 797 837 echo ('\n===> Leaks ===>') 798 echo (' Leak OCE = dOCE_mas_wat + empmr - iceshelf = {:12.3e} (kg) '.format ( dOCE_mas_wat + OCE_mas_empmr - OCE_mas_iceshelf) ) 838 echo (' Leak OCE = dOCE_mas_wat + empmr - iceshelf = {:12.3e} (kg) '.format ( dOCE_mas_wat + OCE_mas_empmr - OCE_mas_iceshelf) ) 799 839 echo (' = (dOCE_mas_wat + empmr - iceshelf)/abs(dOCE_mas_wat) = {:12.1e} (-) '.format ( (dOCE_mas_wat + OCE_mas_empmr - OCE_mas_iceshelf ) / abs (dOCE_mas_wat) ) ) 800 840 echo (' Leak ICE+SNW+PND 1 = dICE_mas_wat + emp_ice + wfxice + wfxsnw + wfxpnd + wfxsub_err = {:12.3e} (kg) '.format ( dICE_mas_wat + OCE_mas_emp_ice + OCE_mas_wfxice + OCE_mas_wfxsnw + ICE_mas_wfxpnd + ICE_mas_wfxsub_err ) ) … … 817 857 echo ('\n===> Checks ===>' ) 818 858 echo (' Check EMPMR = empmr - emp_oce + runoffs + wfxice + wfxsnw + wfxpnd + wfxsub_err = {:12.5e} (kg) '.format ( OCE_mas_empmr - OCE_mas_emp_oce + OCE_mas_runoffs + OCE_mas_wfxice + OCE_mas_wfxsnw + ICE_mas_wfxpnd + ICE_mas_wfxsub_err )) 819 if Coupled : 859 if Coupled : 820 860 echo (' Check EMP_OCE = emp_oce + fwb + ssr - evap_oce + rain + snow_oce + calving = {:12.5e} (kg) '.format ( OCE_mas_emp_oce + OCE_mas_fwb + OCE_mas_ssr - OCE_mas_evap_oce + OCE_mas_rain + OCE_mas_snow_oce + OCE_mas_calving )) 821 861 else : … … 836 876 echo ( 'Freshwater flux at the interface ocean-atm = emp_oce + calving - vfxsub_err = {:12.5e} (kg) '.format ( OCE_mas_emp_oce + OCE_mas_calving - ICE_mas_wfxsub_err )) 837 877 838 echo ( "scsshtot : global_average_sea_level_change = {:12.3e} (m) ".format ( np.sum (d_OCE_sca['scsshtot'] ).values ) ) 839 echo ( "scsshtot : global_average_sea_level_change = {:12.3e} (kg)".format ( np.sum (d_OCE_sca['scsshtot'] ).values * OCE_aire_tot*OCE_rho_liq ) ) 840 echo ( "bgvolssh : drift in global mean ssh volume wrt timestep 1 = {:12.3e} (kg)".format ( np.sum (d_OCE_sca['bgvolssh'] ).values * 1e9 * OCE_rho_liq ) ) 841 echo ( "bgvole3t : drift in global mean volume variation (e3t) wrt timestep 1 = {:12.3e} (kg)".format ( np.sum (d_OCE_sca['bgvole3t'] ).values * 1e9 * OCE_rho_liq ) ) 842 echo ( "bgfrcvol : global mean volume from forcing = {:12.3e} (kg)".format ( np.sum (d_OCE_sca['bgfrcvol'] ).values * 1e9 * OCE_rho_liq ) ) 843 echo ( "ibgvol_tot : global mean ice volume = {:12.3e} (kg)".format ( np.sum (d_OCE_sca['ibgvol_tot']).values * 1e9 * OCE_rho_liq ) ) 844 echo ( "sbgvol_tot : global mean snow volume = {:12.3e} (kg)".format ( np.sum (d_OCE_sca['sbgvol_tot']).values * 1e9 * OCE_rho_liq ) ) 845 echo ( "ibgvolume : drift in ice/snow volume (equivalent ocean volume) = {:12.3e} (kg)".format ( np.sum (d_OCE_sca['ibgvolume'] ).values * 1e9 * OCE_rho_liq ) ) 846 878 echo ( "scsshtot : global_average_sea_level_change = {:12.3e} (m) ".format ( np.sum (d_OCE_sca['scsshtot'] ) ) ) 879 echo ( "scsshtot : global_average_sea_level_change = {:12.3e} (kg)".format ( np.sum (d_OCE_sca['scsshtot'] ) * OCE_aire_tot*OCE_RHO_LIQ ) ) 880 echo ( "bgvolssh : drift in global mean ssh volume wrt timestep 1 = {:12.3e} (kg)".format ( np.sum (d_OCE_sca['bgvolssh'] ) * 1e9 * OCE_RHO_LIQ ) ) 881 echo ( "bgvole3t : drift in global mean volume variation (e3t) wrt timestep 1 = {:12.3e} (kg)".format ( np.sum (d_OCE_sca['bgvole3t'] ) * 1e9 * OCE_RHO_LIQ ) ) 882 echo ( "bgfrcvol : global mean volume from forcing = {:12.3e} (kg)".format ( np.sum (d_OCE_sca['bgfrcvol'] ) * 1e9 * OCE_RHO_LIQ ) ) 883 echo ( "ibgvol_tot : global mean ice volume = {:12.3e} (kg)".format ( np.sum (d_OCE_sca['ibgvol_tot']) * 1e9 * OCE_RHO_LIQ ) ) 884 echo ( "sbgvol_tot : global mean snow volume = {:12.3e} (kg)".format ( np.sum (d_OCE_sca['sbgvol_tot']) * 1e9 * OCE_RHO_LIQ ) ) 885 echo ( "ibgvolume : drift in ice/snow volume (equivalent ocean volume) = {:12.3e} (kg)".format ( np.sum (d_OCE_sca['ibgvolume'] ) * 1e9 * OCE_RHO_LIQ ) ) 886 887 echo ( ' ' ) 888 echo ( f'{Title = }' ) 889 890 echo ( 'SVN Information' ) 891 for kk in SVN.keys(): 892 print ( SVN[kk] ) -
TOOLS/WATER_BUDGET/WaterUtils.py
r6647 r6665 2 2 ''' 3 3 Script to check water conservation in the IPSL coupled model 4 4 5 5 Here, some common utilities to all scripts 6 6 7 7 Warning, to install, configure, run, use any of included software or 8 8 to read the associated documentation you'll need at least one (1) … … 18 18 $Date$ 19 19 $Revision$ 20 $Id: ATM_waterbudget.py 6277 2022-12-08 09:24:05Z omamce$20 $Id: $ 21 21 $HeadURL$ 22 22 ''' 23 23 24 import functools 25 import time 26 import configparser, re 24 27 import numpy as np 25 import configparser, re 26 27 VarInt = 10 28 Rho = 1000.0 29 Ra = 6366197.7236758135 #-- Earth Radius (m) 30 Grav = 9.81 #-- Gravity (m^2/s 31 ICE_rho_ice = 917.0 #-- Ice volumic mass (kg/m3) in LIM3 32 ICE_rho_sno = 330.0 #-- Snow volumic mass (kg/m3) in LIM3 33 OCE_rho_liq = 1026.0 #-- Ocean water volumic mass (kg/m3) in NEMO 34 ATM_rho = 1000.0 #-- Water volumic mass in atmosphere (kg/m^3) 35 SRF_rho = 1000.0 #-- Water volumic mass in surface reservoir (kg/m^3) 36 RUN_rho = 1000.0 #-- Water volumic mass of rivers (kg/m^3) 37 ICE_rho_pnd = 1000. #-- Water volumic mass in ice ponds (kg/m^3) 38 YearLength = 365.25 * 24. * 60. * 60. #-- Year length (s) 28 29 VAR_INT = 10 30 RHO = 1000.0 31 RA = 6366197.7236758135 #-- Earth Radius (m) 32 GRAV = 9.81 #-- Gravity (m^2/s 33 ICE_RHO_ICE = 917.0 #-- Ice volumic mass (kg/m3) in LIM3 34 ICE_RHO_SNO = 330.0 #-- Snow volumic mass (kg/m3) in LIM3 35 OCE_RHO_LIQ = 1026.0 #-- Ocean water volumic mass (kg/m3) in NEMO 36 ATM_RHO = 1000.0 #-- Water volumic mass in atmosphere (kg/m^3) 37 SRF_RHO = 1000.0 #-- Water volumic mass in surface reservoir (kg/m^3) 38 RUN_RHO = 1000.0 #-- Water volumic mass of rivers (kg/m^3) 39 ICE_RHO_PND = 1000. #-- Water volumic mass in ice ponds (kg/m^3) 40 YEAR_LENGTH = 365.25 * 24. * 60. * 60. #-- Year length (s) 39 41 40 42 def setBool (chars) : 41 43 '''Convert specific char string in boolean if possible''' 42 setBool = chars43 if type (chars) == str:44 z_set_bool = chars 45 if isinstance (chars, str) : 44 46 for key in configparser.ConfigParser.BOOLEAN_STATES.keys () : 45 if chars.lower() == key : setBool = configparser.ConfigParser.BOOLEAN_STATES[key] 46 return setBool 47 if chars.lower() == key : 48 z_set_bool = configparser.ConfigParser.BOOLEAN_STATES[key] 49 return z_set_bool 47 50 48 51 def setNum (chars) : 49 52 '''Convert specific char string in integer or real if possible''' 50 if type (chars) == str:51 realnum = re.compile ("^[-+]?[0-9]*\.?[0-9]+(e[-+]?[0-9]+)?$")52 is Number = realnum.match(chars.strip()) != None53 is Int = chars.strip().isdigit()54 #print (chars , isNumber , isInt)55 if isNumber :56 if isInt : setNum = int(chars)57 else : setNum = float (chars)58 else : setNum = chars59 else : setNum = chars60 return setNum53 if isinstance (chars, str) : 54 realnum = re.compile ("^[-+]?[0-9]*\.?[0-9]+(e[-+]?[0-9]+)?$") 55 is_number = realnum.match(chars.strip()) != None 56 is_int = chars.strip().isdigit() 57 if is_number : 58 if is_int : zset_num = int (chars) 59 else : zset_num = float (chars) 60 else : zset_num = chars 61 else : 62 zset_num = chars 63 return zset_num 61 64 62 65 def setNone (chars) : 63 66 '''Convert specific char string to None if possible''' 64 if type (chars) == str:65 if chars in ['None', 'NONE', 'none'] : setNone = None66 else : setNone = chars67 else : setNone = chars68 return setNone67 if isinstance (chars, str) : 68 if chars in ['None', 'NONE', 'none'] : zset_none = None 69 else : zset_none = chars 70 else : zset_none = chars 71 return zset_none 69 72 70 73 def unDefined (char) : 71 74 '''True if a variable is not defined, or set to None''' 72 75 if char in globals () : 73 if globals()[char] ==None :74 unDefined = True75 else : unDefined = False76 else : unDefined = True77 return unDefined76 if globals()[char] is None : 77 zun_defined = True 78 else : zun_defined = False 79 else : zun_defined = True 80 return zun_defined 78 81 79 82 def Defined (char) : 80 83 '''True if a variable is defined and not equal to None''' 81 84 if char in globals () : 82 if globals()[char] ==None :83 Defined = False84 else : Defined = True85 else : Defined = False86 return Defined85 if globals()[char] is None : 86 zdefined = False 87 else : zdefined = True 88 else : zdefined = False 89 return zdefined 87 90 88 91 def Ksum (tab) : 89 ''' 90 Kahan summation algorithm, also known as compensated summation 92 '''Kahan summation algorithm, also known as compensated summation 93 91 94 https://en.wikipedia.org/wiki/Kahan_summation_algorithm 92 95 ''' 93 Ksum = 0.0 # Prepare the accumulator. 94 comp = 0.0 # A running compensation for lost low-order bits. 95 96 for xx in np.where ( np.isnan(tab), 0., tab ) : 97 yy = xx - comp # comp is zero the first time around. 98 tt = Ksum + yy # Alas, sum is big, y small, so low-order digits of y are lost. 99 comp = (tt - Ksum) - yy # (tt - Ksum) cancels the high-order part of y; subtracting y recovers negative (low part of yy) 100 Ksum = tt # Algebraically, comp should always be zero. Beware overly-aggressive optimizing compilers ! 101 # Next time around, the lost low part will be added to y in a fresh attempt. 102 return Ksum 96 # Prepare the accumulator. 97 ksum = 0.0 98 # A running compensation for lost low-order bits. 99 comp = 0.0 100 101 for xx in np.where ( np.isnan(tab), 0., tab ) : 102 # comp is zero the first time around. 103 yy = xx - comp 104 # Alas, sum is big, y small, so low-order digits of y are lost. 105 tt = ksum + yy 106 # (tt - Ksum) cancels the high-order part of y; subtracting y recovers negative (low part of yy) 107 comp = (tt - ksum) - yy 108 # Algebraically, comp should always be zero. Beware overly-aggressive optimizing compilers ! 109 ksum = tt 110 # Next time around, the lost low part will be added to y in a fresh attempt. 111 112 return ksum 103 113 104 114 def Ssum (tab) : 105 ''' 106 Precision summation by sorting by absolute values 107 ''' 108 Ssum = np.sum ( tab[np.argsort(np.abs(tab))] ) 109 return Ssum 115 '''Precision summation by sorting by absolute values''' 116 ssum = np.sum ( tab[np.argsort(np.abs(tab))] ) 117 return ssum 110 118 111 119 def KSsum (tab) : 112 ''' 113 Precision summation by sorting by absolute value, and applying Kahan summation 114 ''' 115 KSsum = Ksum ( tab[np.argsort(np.abs(tab))] ) 116 return KSsum 120 '''Precision summation by sorting by absolute value, and applying Kahan summation''' 121 kssum = Ksum ( tab[np.argsort(np.abs(tab))] ) 122 return kssum 117 123 118 124 # Choosing algorithm for precise summation 119 125 Psum = KSsum 120 126 121 def IsLeapYear ( Year, CalendarType="Gregorian" ) : 122 ''' True is Year is a leap year ''' 127 def IsLeapYear ( year, CalendarType="Gregorian" ) : 128 '''True is Year is a leap year''' 129 year = int ( year ) 130 zis_leap_year = None 123 131 124 132 # What is the calendar : 125 if CalendarType in [ '360d', '360_day', 'noleap', '365_day'] : 126 IsLeapYear = False 127 return IsLeapYear 128 129 if CalendarType in [ 'all_leap', '366_day' ] : 130 IsLeapYear = True 131 return IsLeapYear 132 133 Year = int ( Year ) 134 133 if CalendarType in [ '360d', '360_day', 'noleap', '365_day'] : 134 zis_leap_year = False 135 136 if CalendarType in [ 'all_leap', '366_day' ] : 137 zis_leap_year = True 138 135 139 # a year is a leap year if it is even divisible by 4 136 140 # but not evenly divisible by 100 … … 138 142 139 143 # if it is evenly divisible by 400 it must be a leap year 140 if np.mod ( Year, 400 ) == 0 : 141 IsLeapYear = True 142 return IsLeapYear 143 144 if not zis_leap_year and np.mod ( year, 400 ) == 0 : 145 zis_leap_year = True 146 144 147 # if it is evenly divisible by 100 it must not be a leap year 145 if np.mod ( Year, 100 ) == 0 : 146 IsLeapYear = False 147 return IsLeapYear 148 if not zis_leap_year and np.mod ( year, 100 ) == 0 : 149 zis_leap_year = False 148 150 149 151 # if it is evenly divisible by 4 it must be a leap year 150 if np.mod ( Year, 4 ) == 0 : 151 IsLeapYear = True 152 return IsLeapYear 153 154 IsLeapYear = False 155 return IsLeapYear 156 157 def DateFormat ( Date ) : 158 ''' 159 Get date format : 152 if not zis_leap_year and np.mod ( year, 4 ) == 0 : 153 zis_leap_year = True 154 155 if not zis_leap_year : 156 zis_leap_year = False 157 158 return zis_leap_year 159 160 def DateFormat ( date ) : 161 '''Get date format : 162 160 163 [yy]yymmdd is Gregorian 161 164 [yy]yy-mm-dd is Human 162 165 ''' 163 if type (Date) == str : 164 if '-' in Date : 165 DateFormat = 'Human' 166 else : 167 DateFormat = 'Gregorian' 168 if type (Date) == int : DateFormat = 'Gregorian' 169 return DateFormat 170 171 def PrintDate ( YE, MO, DA, Format ) : 172 '''Return a date in the requested format''' 173 if Format == 'Human' : PrintDate = f'{YE:04d}-{MO:02d}-{DA:02d}' 174 if Format == 'Gregorian' : PrintDate = f'{YE:04d}{MO:02d}{DA:02d}' 175 return PrintDate 176 177 def FormatToGregorian ( Date ) : 178 ''' from a yyyy-mm-dd or yyymmdd date format returns 166 if isinstance (date, str) : 167 if '-' in date : 168 zdate_format = 'Human' 169 else : 170 zdate_format = 'Gregorian' 171 if isinstance (date, int) : zdate_format = 'Gregorian' 172 return zdate_format 173 174 def PrintDate ( ye, mo, da, pformat ) : 175 '''Return a date in the requested format 176 ''' 177 if pformat == 'Human' : zPrintDate = f'{ye:04d}-{mo:02d}-{da:02d}' 178 if pformat == 'Gregorian' : zPrintDate = f'{ye:04d}{mo:02d}{da:02d}' 179 return zPrintDate 180 181 def FormatToGregorian ( date ) : 182 '''From a yyyy-mm-dd or yyymmdd date format returns 179 183 a yyymmdd date format 180 184 ''' 181 YE, MO, DA = SplitDate ( Date ) 182 FormatToGregorian = f'{YE:04d}{MO:02d}{DA:02d}' 183 return FormatToGregorian 184 185 def FormatToHuman ( Date ) : 186 ''' From a yyyymmdd or yyymmdd date format returns 185 ye, mo, da = SplitDate ( date ) 186 return f'{ye:04d}{mo:02d}{da:02d}' 187 188 def FormatToHuman ( date ) : 189 '''From a yyyymmdd or yyymmdd date format returns 187 190 a yyy-mm-dd date format 188 191 ''' 189 YE, MO, DA = SplitDate ( Date ) 190 FormatToHuman = f'{YE_new:04d}-{MO:02d}-{DA:02d}' 191 return FormatToHuman 192 193 def SplitDate ( Date, Out='int' ) : 194 ''' Split Date in format [yy]yymmdd or [yy]yy-mm-dd 195 to yy, mm, dd ''' 196 if type (Date) == str : 197 if '-' in Date : 198 Dash = True 199 YE, MO, DA = Date.split ('-') 200 else : 201 Dash = False 202 YE = Date[:-4] ; MO = Date[-4:-2] ; DA = Date[-2:] 203 YearDigits = len (YE) 204 if type (Date) == int : 205 DA = np.mod ( Date, 100) 206 MO = np.mod ( Date//100, 100) 207 YE = Date // 10000 192 ye, mo, da = SplitDate ( date ) 193 return f'{ye:04d}-{mo:02d}-{da:02d}' 194 195 def SplitDate ( date ) : 196 '''Split Date in format [yy]yymmdd or [yy]yy-mm-dd 197 to yy, mm, dd ''' 198 if isinstance (date, str) : 199 if '-' in date : 200 ye, mo, da = date.split ('-') 201 else : 202 ye, mo, da = date[:-4], date[-4:-2], date[-2:] 203 if isinstance (date, int) : 204 da = np.mod ( date, 100) 205 mo = np.mod ( date//100, 100) 206 ye = date // 10000 207 208 ye = int(ye) ; mo = int(mo) ; da=int(da) 209 return ye, mo, da 210 211 def DateAddYear ( date, year_inc=1 ) : 212 '''Add on year(s) to date in format [yy]yymmdd or [yy]yy-mm-dd''' 213 zformat = DateFormat ( date ) 214 ye, mo, da = SplitDate ( date ) 215 ye_new = ye + year_inc 216 return PrintDate ( ye_new, mo, da, zformat) 217 218 def DateMinusOneDay ( date ) : 219 '''Substracts one day to date in format [yy]yymmdd or [yy]yy-mm-dd''' 220 zformat = DateFormat ( date ) 221 mth_length = np.array ( [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] ) 222 ye, mo, da = SplitDate ( date ) 223 if IsLeapYear ( ye) : mth_length[1] += 1 224 225 ye = int(ye) ; mo = int(mo) ; da=int(da) 226 if da == 1 : 227 if mo == 1 : 228 da_new, mo_new, ye_new = mth_length[-1 ], 12 , ye - 1 229 else : 230 da_new, mo_new, ye_new = mth_length[mo-2], mo - 1, ye 231 else : 232 da_new, mo_new, ye_new = da - 1, mo, ye 233 234 return PrintDate ( ye_new, mo_new, da_new, zformat) 235 236 def DatePlusOneDay ( date ) : 237 '''Add one day to date in format [yy]yymmdd or [yy]yy-mm-dd''' 238 zformat = DateFormat ( date ) 239 mth_length = np.array ( [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] ) 240 ye, mo, da = SplitDate ( date ) 241 if IsLeapYear ( ye ) : mth_length[1] += 1 242 243 ye_new = ye 244 mo_new = mo 245 da_new = da+1 246 if da_new > mth_length [mo_new-1] : 247 da_new = 1 248 mo_new = mo_new + 1 249 if mo_new == 13 : 250 mo_new = 1 251 ye_new += 1 252 253 return PrintDate ( ye_new, mo_new, da_new, zformat ) 254 255 class Timer : 256 '''Measures execution time of a function''' 257 def __str__(self): 258 return str(self.__class__) 208 259 209 YE = int(YE) ; MO = int(MO) ; DA=int(DA) 210 return YE, MO, DA 211 212 def DateAddYear ( Date, YearInc=1 ) : 213 ''' Add on year to date in format [yy]yymmdd or [yy]yy-mm-dd''' 214 Format = DateFormat ( Date ) 215 YE, MO, DA = SplitDate ( Date ) 216 YE_new = YE + YearInc 217 DateAddYear = PrintDate ( YE_new, MO, DA, Format) 218 return DateAddYear 219 220 def DateMinusOneDay ( Date ) : 221 ''' Substracts one day to date in format [yy]yymmdd or [yy]yy-mm-dd''' 222 Format = DateFormat ( Date ) 223 mth_length = np.array ( [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] ) 224 YE, MO, DA = SplitDate ( Date ) 225 if IsLeapYear ( YE) : mth_length[1] += 1 260 def __name__(self): 261 return self.__class__.__name__ 262 263 def __init__ (self, func, debug=False, timer=True) : 264 functools.update_wrapper (self, func) 265 self.func = func 266 self.num_calls = 0 267 self.cumul_time = 0. 268 self.debug = debug 269 self.timer = timer 270 271 def __call__ (self, *args, **kwargs) : 272 self.num_calls += 1 273 if self.debug : 274 print ( f'>-- Calling {self.__name__} --------------------------------------------------' ) 275 args_repr = [f"{repr (a)}" for a in args] 276 kwargs_repr = [f"{k}={v!r}" for k, v in kwargs.items ()] 277 signature = ", ".join (args_repr + kwargs_repr) 278 print ( f"Signature : ({signature}") 279 start_time = time.perf_counter () 280 values = self.func (*args, **kwargs) 281 end_time = time.perf_counter () 282 run_time = end_time - start_time 283 self.cumul_time += run_time 284 if self.timer : 285 print ( f"--> Calling {self.__name__!r} : {run_time:.3f} secs / cumul {self.cumul_time:.3f} # {self.num_calls:2d} calls") 286 if self.debug : 287 print ( '<------------------------------------------------------------' ) 288 return values 226 289 227 YE = int(YE) ; MO = int(MO) ; DA=int(DA)228 if DA == 1 :229 if MO == 1 : DA_new = mth_length[-1] ; MO_new = 12 ; YE_new = YE - 1230 else : DA_new = mth_length[MO-2] ; MO_new = MO - 1 ; YE_new = YE231 else : DA_new = DA - 1 ; MO_new = MO ; YE_new = YE232 233 DateMinusOneDay = PrintDate ( YE_new, MO_new, DA_new, Format)234 return DateMinusOneDay235 236 def DatePlusOneDay ( Date ) :237 ''' Add one day to date in format [yy]yymmdd or [yy]yy-mm-dd'''238 Format = DateFormat ( Date )239 mth_length = np.array ( [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] )240 YE, MO, DA = SplitDate ( Date )241 if IsLeapYear ( YE ) : mth_length[1] += 1242 243 YE_new = YE ; MO_new=MO ; DA_new=DA+1244 if DA_new > mth_length [MO_new-1] :245 DA_new = 1 ; MO_new = MO_new + 1246 if MO_new == 13 :247 MO_new = 1 ; YE_new = YE_new+1248 249 DatePlusOneDay = PrintDate ( YE_new, MO_new, DA_new, Format )250 return DatePlusOneDay -
TOOLS/WATER_BUDGET/lmdz.py
r6647 r6665 21 21 Utilities for LMDZ grid 22 22 23 olivier.marti@lsce.ipsl.fr 24 ''' 23 - Lots of tests for xarray object 24 - Not much tested for numpy objects 25 26 Author: olivier.marti@lsce.ipsl.fr 25 27 26 28 ## SVN information … … 30 32 __Id__ = "$Id: $" 31 33 __HeadURL = "$HeadURL$" 34 ''' 32 35 33 36 import numpy as np 34 35 try : import xarray as xr 36 except ImportError : pass 37 38 try : import numba 39 except ImportError : pass 40 41 rpi = np.pi ; rad = np.deg2rad (1.0) ; dar = np.rad2deg (1.0) 42 43 44 def __mmath__ (tab) : 45 ''' 46 Determines the type of tab : i.e. numpy or xarray 47 ''' 48 math = None 49 try : 50 if type (tab) == xr.core.dataarray.DataArray : math = xr 51 except : 52 pass 53 54 try : 55 if type (tab) == np.ndarray : math = np 56 except : 57 pass 58 59 return math 37 import xarray as xr 38 39 RPI = np.pi 40 RAD = np.deg2rad (1.0) 41 DAR = np.rad2deg (1.0) 42 REPSI = np.finfo (1.0).eps 43 44 RAAMO = 12 # Number of months in one year 45 RJJHH = 24 # Number of hours in one day 46 RHHMM = 60 # Number of minutes in one hour 47 RMMSS = 60 # Number of seconds in one minute 48 RA = 6371229.0 # Earth radius [m] 49 GRAV = 9.80665 # Gravity [m/s2] 50 RT0 = 273.15 # Freezing point of fresh water [Kelvin] 51 RAU0 = 1026.0 # Volumic mass of sea water [kg/m3] 52 RLEVAP = 2.5e+6 # Latent heat of evaporation (water) [J/K] 53 VKARMN = 0.4 # Von Karman constant 54 STEFAN = 5.67e-8 # Stefan-Boltzmann constant [W/m2/K4] 55 #RHOS = 330. # Volumic mass of snow [kg/m3] 56 57 RDAY = RJJHH * RHHMM * RMMSS # Day length [s] 58 RSIYEA = 365.25 * RDAY * 2. * RPI / 6.283076 # Sideral year length [s] 59 RSIDAY = RDAY / (1. + RDAY / RSIYEA) # Sideral day length [s] 60 ROMEGA = 2. * RPI / RSIDAY # Earth rotation parameter [s-1] 61 62 def __mmath__ (ptab, default=None) : 63 '''Determines the type of tab : xarray, numpy or numpy.ma object ? 64 65 Returns type 66 ''' 67 mmath = default 68 if isinstance (ptab, xr.core.dataarray.DataArray) : 69 mmath = xr 70 if isinstance (ptab, np.ndarray) : 71 mmath = np 72 if isinstance (ptab, np.ma.MaskType) : 73 mmath = np.ma 74 75 return mmath 76 60 77 # 61 78 def extend (tab, Lon=False, jplus=25, jpi=None, lonplus=360.0) : 62 ''' 63 Returns extended field eastward to have better plots, and box average crossing the boundary 79 '''Returns extended field eastward to have better plots, and box average crossing the boundary 80 64 81 Works only for xarray and numpy data (?) 65 82 … … 69 86 size of the field != jpi (avoid to extend several times) 70 87 jplus (optional, default=25) : number of points added on the east side of the field 88 ''' 71 89 72 '''73 90 math = __mmath__ (tab) 74 if tab.shape[-1] == 1 : extend = tab 75 76 else : 77 if jpi == None : jpi = tab.shape[-1] 78 79 if Lon : xplus = lonplus 80 else : xplus = 0.0 91 if tab.shape[-1] == 1 : 92 ztab = tab 93 94 else : 95 if jpi is None : 96 jpi = tab.shape[-1] 97 98 if Lon : 99 xplus = lonplus 100 else : 101 xplus = 0.0 81 102 82 103 if tab.shape[-1] > jpi : 83 extend= tab104 ztab = tab 84 105 else : 85 istart = 0 ; le=jpi+1 ; la=0 106 istart = 0 107 le = jpi+1 108 la = 0 86 109 if math == xr : 87 110 lon = tab.dims[-1] 88 extend = xr.concat ((tab.isel(lon=slice(istart ,istart+le )), 89 tab.isel(lon=slice(istart+la,istart+la+jplus))+xplus ), dim=lon) 90 try : 91 extend_lon = xr.concat ((tab.coords[lon].isel(lon=slice(istart ,istart+le )), 92 tab.coords[lon].isel(lon=slice(istart+la,istart+la+jplus))+lonplus), dim=lon) 93 extend = extend.assign_coords ( {tab.dims[-1]:extend_lon} ) 94 except : 95 pass 111 ztab = xr.concat (( 112 tab.isel (lon=slice(istart ,istart+le )), 113 tab.isel (lon=slice(istart+la,istart+la+jplus))+xplus ), dim=lon) 114 if 'lon' in tab.dims : 115 extend_lon = xr.concat (( 116 tab.coords[lon].isel(lon=slice(istart ,istart+le )), 117 tab.coords[lon].isel(lon=slice(istart+la,istart+la+jplus))+lonplus), dim=lon) 118 ztab = ztab.assign_coords ( {tab.dims[-1]:extend_lon} ) 119 #except : 120 # pass 96 121 if math == np : 97 extend = np.concatenate ((tab [..., istart:istart+le], tab [..., istart+la:jplus]+xplus ), axis=-1) 98 99 return extend 122 ztab = np.concatenate ((tab [..., istart:istart+le], 123 tab [..., istart+la:jplus]+xplus ), axis=-1) 124 125 return ztab 100 126 101 127 def interp1d (x, xp, yp, zdim='presnivs', units=None, verbose=False, method='linear') : 102 ''' 103 One-dimensionnal interpolation of a multi-dimensionnal field 128 '''One-dimensionnal interpolation of a multi-dimensionnal field 104 129 105 130 Intended to interpolate on standard pressure level 106 131 107 132 All inputs shoud be xarray data arrays 108 133 109 Input : 134 Input : 110 135 x : levels at wich we want to interpolate (i.e. standard pressure levels) 111 136 xp : position of the input points (i.e. pressure) … … 117 142 nearest : nearest neighbor 118 143 119 \!/ xp should be decreasing values along zdim axis \!/144 Warning : xp should be decreasing values along zdim axis 120 145 ''' 121 146 # Get the number of dimension with dim==zdim 122 147 axis = list(xp.dims).index(zdim) 123 148 124 149 # Get the number of levels in each arrays 125 nk_in = xp.shape[axis]126 150 nk_ou = len (x) 127 151 128 152 # Define the result array 129 153 in_shape = np.array (xp.shape) 130 if verbose : print ( f'{in_shape=}' ) 154 if verbose : 155 print ( f'{in_shape=}' ) 131 156 ou_shape = np.array (in_shape) 132 if verbose : print ( f'{ou_shape=}' ) 157 if verbose : 158 print ( f'{ou_shape=}' ) 133 159 ou_shape[axis] = nk_ou 134 160 135 161 in_dims = list (yp.dims) 136 if verbose : print ( f'{in_dims=}' ) 162 if verbose : 163 print ( f'{in_dims=}' ) 137 164 ou_dims = in_dims 138 165 139 166 pdim = x.dims[0] 140 167 ou_dims[axis] = pdim … … 144 171 if dim == zdim : 145 172 ou_dims[i] = x.dims[0] 146 if units != None : yp[dim].attrs['units'] = units 173 if units is not None : 174 yp[dim].attrs['units'] = units 147 175 new_coords.append (x .values) 148 176 else : 149 177 new_coords.append (yp.coords[dim].values) 150 178 151 179 if verbose : 152 180 print ( f'{ou_dims =}' ) 153 181 print ( f'{new_coords=}' ) 154 182 155 183 ou_tab = xr.DataArray (np.empty (ou_shape), dims=ou_dims, coords=new_coords) 156 184 157 185 if 'log' in method : 158 yp_min = yp.min() ; yp_max = yp.max() 159 indic = yp_min * yp_max 186 yp_min = yp.min() 187 yp_max = yp.max() 188 indic = yp_min * yp_max 160 189 if indic <= 0. : 161 print ( 'Input data have a change of sign')162 print ( 'Error: logarithmic method is available only for')163 print ( 'positive or negative input values. ')164 raise Exception190 print ( 'Input data have a change of sign') 191 print ( 'Error: logarithmic method is available only for') 192 print ( 'positive or negative input values. ') 193 raise ValueError 165 194 166 195 # Optimized (pre-compiled) interpolation loop 167 196 #@numba.jit (nopython=True) 168 def __interp ( nk_ou,x, xp, yp) :197 def __interp (x, xp, yp) : 169 198 # Interpolate 170 199 # Find index of the just above level … … 172 201 idk2 = idk1 - 1 173 202 idk2 = np.maximum (idk2, 0) 174 203 175 204 x1 = xp[{zdim:idk1}] 176 205 x2 = xp[{zdim:idk2}] 177 206 178 207 dx1 = x - x1 179 208 dx2 = x2 - x 180 209 dx = x2 - x1 181 dx1 = dx1/dx ; dx2 = dx2/dx 182 210 dx1 = dx1/dx 211 dx2 = dx2/dx 212 183 213 y1 = yp[{zdim:idk1}] 184 214 y2 = yp[{zdim:idk2}] 185 186 #print ( f'{k=} {idk1=} {idk2=} {x1=} {x2=} {dx=1} {dx2=} {y1=} {y2}' ) 187 188 if 'linear' in method : 189 result = (dx1*y2 + dx2*y1) 215 216 if 'linear' in method : 217 result = dx1*y2 + dx2*y1 190 218 if 'log' in method : 191 if yp_min > 0. : 219 if yp_min > 0. : 192 220 result = np.power(y2, dx1) * np.power(y1, dx2) 193 221 if yp_max < 0. : … … 195 223 if 'nearest' in method : 196 224 result = xr.where ( dx2>=dx1, y1, y2) 197 225 198 226 return result 199 227 200 228 for k in np.arange (nk_ou) : 201 result = __interp ( nk_ou,x[{pdim:k}], xp, yp)202 229 result = __interp (x[{pdim:k}], xp, yp) 230 203 231 # Put result in the final array 204 232 ou_tab [{pdim:k}] = result … … 207 235 208 236 def fixed_lon (lon, center_lon=0.0) : 209 ''' 210 Returns corrected longitudes for nicer plots 237 '''Returns corrected longitudes for nicer plots 211 238 212 239 lon : longitudes of the grid. At least 1D. … … 216 243 ''' 217 244 mmath = __mmath__ (lon) 218 219 fixed_lon = lon.copy () 220 221 fixed_lon = mmath.where (fixed_lon > center_lon+180., fixed_lon-360.0, fixed_lon) 222 fixed_lon = mmath.where (fixed_lon < center_lon-180., fixed_lon+360.0, fixed_lon) 223 224 start = np.argmax (np.abs (np.diff (fixed_lon, axis=-1)) > 180., axis=-1) 225 fixed_lon [start+1:] += 360. 226 227 return fixed_lon 228 229 def nord2sud (p2D) : 230 ''' 231 Swap north to south a 2D field 232 ''' 233 pout = p2D [..., -1::-1, : ] 245 246 zfixed_lon = lon.copy () 247 248 zfixed_lon = mmath.where (zfixed_lon > center_lon+180., zfixed_lon-360.0, fixed_lon) 249 zfixed_lon = mmath.where (zfixed_lon < center_lon-180., zfixed_lon+360.0, fixed_lon) 250 251 start = np.argmax (np.abs (np.diff (zfixed_lon, axis=-1)) > 180., axis=-1) 252 zfixed_lon [start+1:] += 360. 253 254 return zfixed_lon 255 256 def nord2sud (p2d) : 257 '''Swap north to south a 2D field 258 ''' 259 pout = p2d [..., -1::-1, : ] 234 260 235 261 return pout 236 262 237 def point2geo (p1D) : 238 ''' 239 From 1D (restart type) to 2D 240 ''' 241 math = __mmath__ (p1D) 263 def point2geo (p1d) : 264 '''From 1D (restart type) to 2D 265 ''' 266 math = __mmath__ (p1d) 242 267 243 268 # Get the dimensions 244 jpn = p1 D.shape[-1]245 246 if len (p1 D.shape) > 1 :247 jpt = p1 D.shape[0]269 jpn = p1d.shape[-1] 270 271 if len (p1d.shape) > 1 : 272 jpt = p1d.shape[0] 248 273 else : 249 274 jpt = 0 250 251 if jpn == 9026 : jpi = 96 ; jpj = 96 252 if jpn == 17858 : jpi = 144 ; jpj = 144 253 if jpn == 20306 : jpi = 144 ; jpj = 143 275 276 if jpn == 9026 : 277 jpi, jpj = 96, 96 278 if jpn == 17858 : 279 jpi, jpj = 144, 144 280 if jpn == 20306 : 281 jpi, jpj = 144, 143 254 282 255 283 if jpt > 0 : 256 p2 D= np.zeros ( (jpt, jpj, jpi) )257 p2 D [:, 1:jpj-1, :] = np.reshape (p1D[:,1:jpn-1], (jpt, jpj-2, jpi) )258 p2 D [:, 0 , : ] = p1D[:, 0 ]259 p2 D [:, jpj-1, : ] = p1D[:, jpn-1]260 261 else : 262 p2 D= np.zeros ( (jpj, jpi) )263 p2 D [1:jpj-1, :] = np.reshape (np.float64 (p1D[1:jpn-1]), (jpj-2, jpi) )264 p2 D [0 , : ] = p1D[ 0 ]265 p2 D [jpj-1, : ] = p1D[jpn-1]284 p2d = np.zeros ( (jpt, jpj, jpi) ) 285 p2d [:, 1:jpj-1, :] = np.reshape (p1d [:,1:jpn-1], (jpt, jpj-2, jpi) ) 286 p2d [:, 0 , : ] = p1d[:, 0 ] 287 p2d [:, jpj-1, : ] = p1d[:, jpn-1] 288 289 else : 290 p2d = np.zeros ( (jpj, jpi) ) 291 p2d [1:jpj-1, :] = np.reshape (np.float64 (p1d [1:jpn-1]), (jpj-2, jpi) ) 292 p2d [0 , : ] = p1d[ 0 ] 293 p2d [jpj-1, : ] = p1d[jpn-1] 266 294 267 295 if math == xr : 268 p2D = xr.DataArray (p2D) 269 for attr in p1D.attrs : 270 p2D.attrs[attr] = p1D.attrs[attr] 271 p2D = p2D.rename ( {p2D.dims[0]:p1D.dims[0], p2D.dims[-1]:'x', p2D.dims[-2]:'y'} ) 272 273 return p2D 274 275 def geo2point ( p2D, cumulPoles=False, dim1D='points_physiques' ) : 276 ''' 277 From 2D (lat, lon) to 1D (points_phyiques) 278 ''' 279 math = __mmath__ (p2D) 296 p2d = xr.DataArray (p2d) 297 p2d.attrs.update ( p1d.attrs ) 298 p2d = p2d.rename ( {p2d.dims[0]:p1d.dims[0], p2d.dims[-1]:'x', p2d.dims[-2]:'y'} ) 299 300 return p2d 301 302 def geo2point ( p2d, cumul_poles=False, dim1d='points_physiques' ) : 303 '''From 2D (lat, lon) to 1D (points_phyiques) 304 ''' 305 math = __mmath__ (p2d) 280 306 # 281 307 # Get the dimension 282 283 (jpj, jpi) = p2 D.shape[-2:]284 285 if len (p2 D.shape) > 2 :286 jpt = p2 D.shape[0]287 else : 308 309 (jpj, jpi) = p2d.shape[-2:] 310 311 if len (p2d.shape) > 2 : 312 jpt = p2d.shape[0] 313 else : 288 314 jpt = -1 289 315 … … 291 317 292 318 if jpt == -1 : 293 p1 D= np.zeros ( (jpn,) )294 p1 D[1:-1] = np.float64(p2D[1:-1, :]).ravel()295 p1 D[ 0] = p2D[ 0, 0]296 p1 D[-1] = p2D[-1, 0]297 298 else : 299 p1 D= np.zeros ( (jpt, jpn) )319 p1d = np.zeros ( (jpn,) ) 320 p1d[1:-1] = np.float64(p2d[1:-1, :]).ravel() 321 p1d[ 0] = p2d[ 0, 0] 322 p1d[-1] = p2d[-1, 0] 323 324 else : 325 p1d = np.zeros ( (jpt, jpn) ) 300 326 if math == xr : 301 p1 D[:, 1:-1] = np.reshape ( np.float64 (p2D[:, 1:-1, :].values).ravel(), (jpt, jpn-2) )327 p1d[:, 1:-1] = np.reshape ( np.float64 (p2d[:, 1:-1, :].values).ravel(), (jpt, jpn-2) ) 302 328 else : 303 p1 D[:, 1:-1] = np.reshape ( np.float64 (p2D[:, 1:-1, :] ).ravel(), (jpt, jpn-2) )304 p1 D[:, 0 ] = p2D[:, 0, 0]305 p1 D[:, -1 ] = p2D[:, -1, 0]306 329 p1d[:, 1:-1] = np.reshape ( np.float64 (p2d[:, 1:-1, :] ).ravel(), (jpt, jpn-2) ) 330 p1d[:, 0 ] = p2d[:, 0, 0] 331 p1d[:, -1 ] = p2d[:, -1, 0] 332 307 333 if math == xr : 308 p1D = xr.DataArray (p1D) 309 for attr in p2D.attrs : 310 p1D.attrs[attr] = p2D.attrs[attr] 311 p1D = p1D.rename ( {p1D.dims[0]:p2D.dims[0], p1D.dims[-1]:dim1D} ) 312 313 if cumulPoles : 314 p1D[..., 0] = np.sum ( p2D[..., 0, :] ) 315 p1D[..., -1] = np.sum ( p2D[..., -1, :] ) 316 317 return p1D 318 319 def geo3point ( p3D, cumulPoles=False, dim1D='points_physiques' ) : 320 ''' 321 From 3D (lev, lat, lon) to 2D (lev, points_phyiques) 322 ''' 323 math = __mmath__ (p3D) 334 p1d = xr.DataArray (p1d) 335 p1d.attrs.update ( p2d.attrs ) 336 p1d = p1d.rename ( {p1d.dims[0]:p2d.dims[0], p1d.dims[-1]:dim1d} ) 337 338 if cumul_poles : 339 p1d[..., 0] = np.sum ( p2d[..., 0, :] ) 340 p1d[..., -1] = np.sum ( p2d[..., -1, :] ) 341 342 return p1d 343 344 def geo3point ( p3d, cumul_poles=False, dim1d='points_physiques' ) : 345 '''From 3D (lev, lat, lon) to 2D (lev, points_phyiques) 346 ''' 347 math = __mmath__ (p3d) 324 348 # 325 349 # Get the dimensions 326 327 (jpk, jpj, jpi) = p3 D.shape[-3:]328 329 if len (p3 D.shape) > 3 :330 jpt = p3 D.shape[0]331 else : 350 351 (jpk, jpj, jpi) = p3d.shape[-3:] 352 353 if len (p3d.shape) > 3 : 354 jpt = p3d.shape[0] 355 else : 332 356 jpt = -1 333 357 … … 335 359 336 360 if jpt == -1 : 337 p2 D= np.zeros ( (jpk, jpn,) )361 p2d = np.zeros ( (jpk, jpn,) ) 338 362 for jk in np.arange (jpk) : 339 p2 D [jk, :] = geo2point ( p3D [jk,:,:], cumulPoles, dim1D)340 else : 341 p2 D= np.zeros ( (jpt, jpk, jpn) )363 p2d [jk, :] = geo2point ( p3d [jk,:,:], cumul_poles, dim1d ) 364 else : 365 p2d = np.zeros ( (jpt, jpk, jpn) ) 342 366 for jk in np.arange (jpk) : 343 p2 D [:, jk, :] = geo2point ( p3D [:, jk,:,:], cumulPoles, dim1D)367 p2d [:, jk, :] = geo2point ( p3d [:, jk,:,:], cumul_poles, dim1d ) 344 368 345 369 if math == xr : 346 p2D = xr.DataArray (p2D) 347 for attr in p2D.attrs : 348 p2D.attrs[attr] = p3D.attrs[attr] 349 p2D = p2D.rename ( {p2D.dims[-1]:dim1D, p2D.dims[-2]:p3D.dims[-3]} ) 350 351 return p2D 352 353 def geo2en (pxx, pyy, pzz, glam, gphi) : 354 ''' 355 Change vector from geocentric to east/north 370 p2d = xr.DataArray (p2d) 371 p2d.attrs.update ( p3d.attrs ) 372 p2d = p2d.rename ( {p2d.dims[-1]:dim1d, p2d.dims[-2]:p3d.dims[-3]} ) 373 374 return p2d 375 376 def geo2en (pxx, pyy, pzz, glam, gphi) : 377 '''Change vector from geocentric to east/north 356 378 357 379 Inputs : … … 360 382 ''' 361 383 362 gsinlon = np.sin ( rad* glam)363 gcoslon = np.cos ( rad* glam)364 gsinlat = np.sin ( rad* gphi)365 gcoslat = np.cos ( rad* gphi)366 384 gsinlon = np.sin (RAD * glam) 385 gcoslon = np.cos (RAD * glam) 386 gsinlat = np.sin (RAD * gphi) 387 gcoslat = np.cos (RAD * gphi) 388 367 389 pte = - pxx * gsinlon + pyy * gcoslon 368 390 ptn = - pxx * gcoslon * gsinlat - pyy * gsinlon * gsinlat + pzz * gcoslat … … 371 393 372 394 def en2geo (pte, ptn, glam, gphi) : 373 ''' 374 Change vector from east/north to geocentric 375 376 Inputs : 395 '''Change vector from east/north to geocentric 396 397 Inputs : 377 398 pte, ptn : eastward/northward components 378 399 glam, gphi : longitude and latitude of the points 379 400 ''' 380 381 gsinlon = np.sin ( rad* glam)382 gcoslon = np.cos ( rad* glam)383 gsinlat = np.sin ( rad* gphi)384 gcoslat = np.cos ( rad* gphi)401 402 gsinlon = np.sin (RAD * glam) 403 gcoslon = np.cos (RAD * glam) 404 gsinlat = np.sin (RAD * gphi) 405 gcoslat = np.cos (RAD * gphi) 385 406 386 407 pxx = - pte * gsinlon - ptn * gcoslon * gsinlat 387 408 pyy = pte * gcoslon - ptn * gsinlon * gsinlat 388 409 pzz = ptn * gcoslat 389 410 390 411 return pxx, pyy, pzz 391 412 -
TOOLS/WATER_BUDGET/nemo.py
r6647 r6665 19 19 ## =========================================================================== 20 20 ''' 21 Utilities to plot NEMO ORCA fields 22 Periodicity and other stuff21 Utilities to plot NEMO ORCA fields, 22 Handles periodicity and other stuff 23 23 24 24 - Lots of tests for xarray object 25 - Not much teste rd for numpy objects26 27 olivier.marti@lsce.ipsl.fr25 - Not much tested for numpy objects 26 27 Author: olivier.marti@lsce.ipsl.fr 28 28 29 29 ## SVN information … … 36 36 37 37 import numpy as np 38 try : import xarray as xr 39 except ImportError : pass 40 41 #try : import f90nml 42 #except : pass 43 44 #try : from sklearn.impute import SimpleImputer 45 #except : pass 46 47 rpi = np.pi ; rad = np.deg2rad (1.0) ; dar = np.rad2deg (1.0) 48 49 nperio_valid_range = [0, 1, 4, 4.2, 5, 6, 6.2] 50 51 rday = 24.*60.*60. # Day length [s] 52 rsiyea = 365.25 * rday * 2. * rpi / 6.283076 # Sideral year length [s] 53 rsiday = rday / (1. + rday / rsiyea) 54 raamo = 12. # Number of months in one year 55 rjjhh = 24. # Number of hours in one day 56 rhhmm = 60. # Number of minutes in one hour 57 rmmss = 60. # Number of seconds in one minute 58 omega = 2. * rpi / rsiday # Earth rotation parameter [s-1] 59 ra = 6371229. # Earth radius [m] 60 grav = 9.80665 # Gravity [m/s2] 61 repsi = np.finfo (1.0).eps 38 import xarray as xr 39 40 try : 41 from sklearn.impute import SimpleImputer 42 except ImportError as err : 43 print ("Import error of sklearn.impute.SimpleImputer :", err) 44 SimpleImputer = None 45 46 try : 47 import f90nml 48 except ImportError as err : 49 print ("Import error of f90nml :", err) 50 f90nml = None 51 52 53 RPI = np.pi 54 RAD = np.deg2rad (1.0) 55 DAR = np.rad2deg (1.0) 56 REPSI = np.finfo (1.0).eps 57 58 NPERIO_VALID_RANGE = [0, 1, 4, 4.2, 5, 6, 6.2] 59 60 RAAMO = 12 # Number of months in one year 61 RJJHH = 24 # Number of hours in one day 62 RHHMM = 60 # Number of minutes in one hour 63 RMMSS = 60 # Number of seconds in one minute 64 RA = 6371229.0 # Earth radius [m] 65 GRAV = 9.80665 # Gravity [m/s2] 66 RT0 = 273.15 # Freezing point of fresh water [Kelvin] 67 RAU0 = 1026.0 # Volumic mass of sea water [kg/m3] 68 SICE = 6.0 # Salinity of ice (for pisces) [psu] 69 SOCE = 34.7 # Salinity of sea (for pisces and isf) [psu] 70 RLEVAP = 2.5e+6 # Latent heat of evaporation (water) [J/K] 71 VKARMN = 0.4 # Von Karman constant 72 STEFAN = 5.67e-8 # Stefan-Boltzmann constant [W/m2/K4] 73 RHOS = 330. # Volumic mass of snow [kg/m3] 74 RHOI = 917. # Volumic mass of sea ice [kg/m3] 75 RHOW = 1000. # Volumic mass of freshwater in melt ponds [kg/m3] 76 RCND_I = 2.034396 # Thermal conductivity of fresh ice [W/m/K] 77 RCPI = 2067.0 # Specific heat of fresh ice [J/kg/K] 78 RLSUB = 2.834e+6 # Pure ice latent heat of sublimation [J/kg] 79 RLFUS = 0.334e+6 # Latent heat of fusion of fresh ice [J/kg] 80 RTMLT = 0.054 # Decrease of seawater meltpoint with salinity 81 82 RDAY = RJJHH * RHHMM * RMMSS # Day length [s] 83 RSIYEA = 365.25 * RDAY * 2. * RPI / 6.283076 # Sideral year length [s] 84 RSIDAY = RDAY / (1. + RDAY / RSIYEA) # Sideral day length [s] 85 OMEGA = 2. * RPI / RSIDAY # Earth rotation parameter [s-1] 62 86 63 87 ## Default names of dimensions 64 dim_names = {'x':'xx', 'y':'yy', 'z':'olevel', 't':None}88 UDIMS = {'x':'x', 'y':'y', 'z':'olevel', 't':'time_counter'} 65 89 66 90 ## All possibles name of dimensions in Nemo files 67 xName = [ 'x', 'X', 'X1', 'xx', 'XX', 'x_grid_T', 'x_grid_U', 'x_grid_V', 'x_grid_F', 'x_grid_W', 'lon', 'nav_lon', 'longitude', 'X1', 'x_c', 'x_f', ] 68 yName = [ 'y', 'Y', 'Y1', 'yy', 'YY', 'y_grid_T', 'y_grid_U', 'y_grid_V', 'y_grid_F', 'y_grid_W', 'lat', 'nav_lat', 'latitude' , 'Y1', 'y_c', 'y_f', ] 69 zName = [ 'z', 'Z', 'Z1', 'zz', 'ZZ', 'depth', 'tdepth', 'udepth', 'vdepth', 'wdepth', 'fdepth', 'deptht', 'depthu', 'depthv', 'depthw', 'depthf', 'olevel', 'z_c', 'z_f', ] 70 tName = [ 't', 'T', 'tt', 'TT', 'time', 'time_counter', 'time_centered', ] 91 XNAME = [ 'x', 'X', 'X1', 'xx', 'XX', 92 'x_grid_T', 'x_grid_U', 'x_grid_V', 'x_grid_F', 'x_grid_W', 93 'lon', 'nav_lon', 'longitude', 'X1', 'x_c', 'x_f', ] 94 YNAME = [ 'y', 'Y', 'Y1', 'yy', 'YY', 95 'y_grid_T', 'y_grid_U', 'y_grid_V', 'y_grid_F', 'y_grid_W', 96 'lat', 'nav_lat', 'latitude' , 'Y1', 'y_c', 'y_f', ] 97 ZNAME = [ 'z', 'Z', 'Z1', 'zz', 'ZZ', 'depth', 'tdepth', 'udepth', 98 'vdepth', 'wdepth', 'fdepth', 'deptht', 'depthu', 99 'depthv', 'depthw', 'depthf', 'olevel', 'z_c', 'z_f', ] 100 TNAME = [ 't', 'T', 'tt', 'TT', 'time', 'time_counter', 'time_centered', ] 71 101 72 102 ## All possibles name of units of dimensions in Nemo files 73 xUnit= [ 'degrees_east', ]74 yUnit= [ 'degrees_north', ]75 zUnit= [ 'm', 'meter', ]76 tUnit= [ 'second', 'minute', 'hour', 'day', 'month', 'year', ]103 XUNIT = [ 'degrees_east', ] 104 YUNIT = [ 'degrees_north', ] 105 ZUNIT = [ 'm', 'meter', ] 106 TUNIT = [ 'second', 'minute', 'hour', 'day', 'month', 'year', ] 77 107 78 108 ## All possibles size of dimensions in Orca files 79 xLength = [ 180, 182, 360, 362]80 yLength= [ 148, 149, 331, 332 ]81 zLength = [31, 75]109 XLENGTH = [ 180, 182, 360, 362, 1440 ] 110 YLENGTH = [ 148, 149, 331, 332 ] 111 ZLENGTH = [ 31, 75] 82 112 83 113 ## =========================================================================== 84 def __mmath__ (tab, default=None) : 85 ''' 86 Determines the type of tab : xarray or numpy object ? 114 def __mmath__ (ptab, default=None) : 115 '''Determines the type of tab : xarray, numpy or numpy.ma object ? 116 117 Returns type 87 118 ''' 88 119 mmath = default 89 try : 90 if type (tab) == xr.core.dataarray.DataArray : mmath = xr 91 except : pass 92 93 try : 94 if type (tab) == np.ndarray : mmath = np 95 except : pass 96 120 if isinstance (ptab, xr.core.dataarray.DataArray) : 121 mmath = xr 122 if isinstance (ptab, np.ndarray) : 123 mmath = np 124 if isinstance (ptab, np.ma.MaskType) : 125 mmath = np.ma 126 97 127 return mmath 98 128 99 def __guess Nperio__ (jpj, jpi, nperio=None, out='nperio') :100 ''' 101 Tries to guess the value of nperio (periodicity parameter. See NEMO documentation for details) 102 129 def __guess_nperio__ (jpj, jpi, nperio=None, out='nperio') : 130 '''Tries to guess the value of nperio (periodicity parameter. 131 132 See NEMO documentation for details) 103 133 Inputs 104 134 jpj : number of latitudes … … 106 136 nperio : periodicity parameter 107 137 ''' 108 if nperio == None : 109 nperio = __guessConfig__ (jpj, jpi, nperio=None, out='nperio') 110 138 if nperio is None : 139 nperio = __guess_config__ (jpj, jpi, nperio=None, out=out) 111 140 return nperio 112 141 113 def __guess Config__ (jpj, jpi, nperio=None, config=None, out='nperio') :114 ''' 115 Tries to guess the value of nperio (periodicity parameter. See NEMO documentation for details) 116 142 def __guess_config__ (jpj, jpi, nperio=None, config=None, out='nperio') : 143 '''Tries to guess the value of nperio (periodicity parameter). 144 145 See NEMO documentation for details) 117 146 Inputs 118 147 jpj : number of latitudes … … 121 150 ''' 122 151 print ( jpi, jpj) 123 if nperio ==None :152 if nperio is None : 124 153 ## Values for NEMO version < 4.2 125 if (jpj == 149 and jpi == 182) or (jpj == None and jpi == 182) or (jpj == 149 or jpi == None) : 126 config = 'ORCA2.3' 127 nperio = 4 # ORCA2. We choose legacy orca2. 128 Iperio = 1 ; Jperio = 0 ; NFold = 1 ; NFtype = 'T' 129 if (jpj == 332 and jpi == 362) or (jpj == None and jpi == 362) or (jpj == 332 and jpi == None) : # eORCA1. 130 config = 'eORCA1.2' 131 nperio = 6 132 Iperio = 1 ; Jperio = 0 ; NFold = 1 ; NFtype = 'F' 154 if ( (jpj == 149 and jpi == 182) or (jpj is None and jpi == 182) or 155 (jpj == 149 or jpi is None) ) : 156 # ORCA2. We choose legacy orca2. 157 config, nperio, iperio, jperio, nfold, nftype = 'ORCA2.3' , 4, 1, 0, 1, 'T' 158 if ((jpj == 332 and jpi == 362) or (jpj is None and jpi == 362) or 159 (jpj == 332 and jpi is None) ) : # eORCA1. 160 config, nperio, iperio, jperio, nfold, nftype = 'eORCA1.2', 6, 1, 0, 1, 'F' 133 161 if jpi == 1442 : # ORCA025. 134 config = 'ORCA025' 135 nperio = 6 136 Iperio = 1 ; Jperio = 0 ; NFold = 1 ; NFtype = 'F' 162 config, nperio, iperio, jperio, nfold, nftype = 'ORCA025' , 6, 1, 0, 1, 'F' 137 163 if jpj == 294 : # ORCA1 138 config = 'ORCA1' 139 nperio = 6 140 Iperio = 1 ; Jperio = 0 ; NFold = 1 ; NFtype = 'F' 141 164 config, nperio, iperio, jperio, nfold, nftype = 'ORCA1' , 6, 1, 0, 1, 'F' 165 142 166 ## Values for NEMO version >= 4.2. No more halo points 143 if (jpj == 148 and jpi == 180) or (jpj == None and jpi == 180) or (jpj == 148 and jpi == None) : 144 config = 'ORCA2.4' 145 nperio = 4.2 # ORCA2. We choose legacy orca2. 146 Iperio = 1 ; Jperio = 0 ; NFold = 1 ; NFtype = 'F' 147 if (jpj == 331 and jpi == 360) or (jpj == None and jpi == 360) or (jpj == 331 and jpi == None) : # eORCA1. 148 config = 'eORCA1.4' 149 nperio = 6.2 150 Iperio = 1 ; Jperio = 0 ; NFold = 1 ; NFtype = 'F' 167 if (jpj == 148 and jpi == 180) or (jpj is None and jpi == 180) or \ 168 (jpj == 148 and jpi is None) : # ORCA2. We choose legacy orca2. 169 config, nperio, iperio, jperio, nfold, nftype = 'ORCA2.4' , 4.2, 1, 0, 1, 'F' 170 if (jpj == 331 and jpi == 360) or (jpj is None and jpi == 360) or \ 171 (jpj == 331 and jpi is None) : # eORCA1. 172 config, nperio, iperio, jperio, nfold, nftype = 'eORCA1.4', 6.2, 1, 0, 1, 'F' 151 173 if jpi == 1440 : # ORCA025. 152 config = 'ORCA025' 153 nperio = 6.2 154 Iperio = 1 ; Jperio = 0 ; NFold = 1 ; NFtype = 'F' 155 156 if nperio == None : 157 raise Exception ('in nemo module : nperio not found, and cannot by guessed') 174 config, nperio, iperio, jperio, nfold, nftype = 'ORCA025' , 6.2, 1, 0, 1, 'F' 175 176 if nperio is None : 177 raise ValueError ('in nemo module : nperio not found, and cannot by guessed') 178 179 if nperio in NPERIO_VALID_RANGE : 180 print ( f'nperio set as {nperio} (deduced from {jpj=} and {jpi=})' ) 158 181 else : 159 if nperio in nperio_valid_range : 160 print ( f'nperio set as {nperio} (deduced from {jpj=} and {jpi=})' ) 161 else : 162 raise ValueError ( f'nperio set as {nperio} (deduced from {jpi=} and {jpj=}) : nemo.py is not ready for this value' ) 163 164 if out == 'nperio' : return nperio 165 if out == 'config' : return config 166 if out == 'perio' : return Iperio, Jperio, NFold, NFtype 167 if out in ['full', 'all'] : return {'nperio':nperio, 'Iperio':Iperio, 'Jperio':Jperio, 'NFold':NFold, 'NFtype':NFtype} 168 169 def __guessPoint__ (ptab) : 170 ''' 171 Tries to guess the grid point (periodicity parameter. See NEMO documentation for details) 172 182 raise ValueError ( f'nperio set as {nperio} (deduced from {jpi=} and {jpj=}) : \n'+ 183 'nemo.py is not ready for this value' ) 184 185 if out == 'nperio' : 186 return nperio 187 if out == 'config' : 188 return config 189 if out == 'perio' : 190 return iperio, jperio, nfold, nftype 191 if out in ['full', 'all'] : 192 return {'nperio':nperio, 'iperio':iperio, 'jperio':jperio, 'nfold':nfold, 'nftype':nftype} 193 194 def __guess_point__ (ptab) : 195 '''Tries to guess the grid point (periodicity parameter. 196 197 See NEMO documentation for details) 173 198 For array conforments with xgcm requirements 174 199 … … 178 203 Credits : who is the original author ? 179 204 ''' 180 181 g P= None205 206 gp = None 182 207 mmath = __mmath__ (ptab) 183 208 if mmath == xr : 184 if 'x_c' in ptab.dims and 'y_c' in ptab.dims : gP = 'T' 185 if 'x_f' in ptab.dims and 'y_c' in ptab.dims : gP = 'U' 186 if 'x_c' in ptab.dims and 'y_f' in ptab.dims : gP = 'V' 187 if 'x_f' in ptab.dims and 'y_f' in ptab.dims : gP = 'F' 188 if 'x_c' in ptab.dims and 'y_c' in ptab.dims and 'z_c' in ptab.dims : gP = 'T' 189 if 'x_c' in ptab.dims and 'y_c' in ptab.dims and 'z_f' in ptab.dims : gP = 'W' 190 if 'x_f' in ptab.dims and 'y_c' in ptab.dims and 'z_f' in ptab.dims : gP = 'U' 191 if 'x_c' in ptab.dims and 'y_f' in ptab.dims and 'z_f' in ptab.dims : gP = 'V' 192 if 'x_f' in ptab.dims and 'y_f' in ptab.dims and 'z_f' in ptab.dims : gP = 'F' 193 194 if gP == None : 195 raise Exception ('in nemo module : cd_type not found, and cannot by guessed') 196 else : 197 print ( f'Grid set as {gP} deduced from dims {ptab.dims}' ) 198 return gP 209 if ('x_c' in ptab.dims and 'y_c' in ptab.dims ) : 210 gp = 'T' 211 if ('x_f' in ptab.dims and 'y_c' in ptab.dims ) : 212 gp = 'U' 213 if ('x_c' in ptab.dims and 'y_f' in ptab.dims ) : 214 gp = 'V' 215 if ('x_f' in ptab.dims and 'y_f' in ptab.dims ) : 216 gp = 'F' 217 if ('x_c' in ptab.dims and 'y_c' in ptab.dims 218 and 'z_c' in ptab.dims ) : 219 gp = 'T' 220 if ('x_c' in ptab.dims and 'y_c' in ptab.dims 221 and 'z_f' in ptab.dims ) : 222 gp = 'W' 223 if ('x_f' in ptab.dims and 'y_c' in ptab.dims 224 and 'z_f' in ptab.dims ) : 225 gp = 'U' 226 if ('x_c' in ptab.dims and 'y_f' in ptab.dims 227 and 'z_f' in ptab.dims ) : 228 gp = 'V' 229 if ('x_f' in ptab.dims and 'y_f' in ptab.dims 230 and 'z_f' in ptab.dims ) : 231 gp = 'F' 232 233 if gp is None : 234 raise AttributeError ('in nemo module : cd_type not found, and cannot by guessed') 235 print ( f'Grid set as {gp} deduced from dims {ptab.dims}' ) 236 return gp 199 237 else : 200 raise Exception('in nemo module : cd_type not found, input is not an xarray data')238 raise AttributeError ('in nemo module : cd_type not found, input is not an xarray data') 201 239 202 240 def get_shape ( ptab ) : 203 ''' 204 Get shape of ptab : 205 shape ma incontain X, Y, Z or T241 '''Get shape of ptab return a string with axes names 242 243 shape may contain X, Y, Z or T 206 244 Y is missing for a latitudinal slice 207 245 X is missing for on longitudinal slice 208 246 etc ... 209 247 ''' 210 211 g et_shape = ''212 i x, ax = __findAxis__ (ptab, 'x')213 jy, ay = __findAxis__ (ptab, 'y')214 kz, az = __findAxis__ (ptab, 'z')215 lt, at = __findAxis__ (ptab, 't')216 if ax : get_shape = 'X'217 if ay : get_shape = 'Y' + get_shape218 if az : get_shape = 'Z' + get_shape219 if at : get_shape = 'T' + get_shape220 return g et_shape221 248 249 g_shape = '' 250 if __find_axis__ (ptab, 'x')[0] : 251 g_shape = 'X' 252 if __find_axis__ (ptab, 'y')[0] : 253 g_shape = 'Y' + g_shape 254 if __find_axis__ (ptab, 'z')[0] : 255 g_shape = 'Z' + g_shape 256 if __find_axis__ (ptab, 't')[0] : 257 g_shape = 'T' + g_shape 258 return g_shape 259 222 260 def lbc_diag (nperio) : 223 lperio = nperio ; aperio = False 261 '''Useful to switch between field with and without halo''' 262 lperio, aperio = nperio, False 224 263 if nperio == 4.2 : 225 lperio = 4 ; aperio =True264 lperio, aperio = 4, True 226 265 if nperio == 6.2 : 227 lperio = 6 ; aperio = True 228 266 lperio, aperio = 6, True 229 267 return lperio, aperio 230 268 231 def __findAxis__ (tab, axis='z') : 232 ''' 233 Find order and name of the requested axis 234 ''' 235 mmath = __mmath__ (tab) 236 ix = None ; ax = None 237 238 if axis in xName : axName = xName ; unList = xUnit ; Length = xLength 239 if axis in yName : axName = yName ; unList = yUnit ; Length = yLength 240 if axis in zName : axName = zName ; unList = zUnit ; Length = zLength 241 if axis in tName : axName = tName ; unList = tUnit ; Length = None 242 269 def __find_axis__ (ptab, axis='z', back=True) : 270 '''Returns name and name of the requested axis''' 271 mmath = __mmath__ (ptab) 272 ax, ix = None, None 273 274 if axis in XNAME : 275 ax_name, unit_list, length = XNAME, XUNIT, XLENGTH 276 if axis in YNAME : 277 ax_name, unit_list, length = YNAME, YUNIT, YLENGTH 278 if axis in ZNAME : 279 ax_name, unit_list, length = ZNAME, ZUNIT, ZLENGTH 280 if axis in TNAME : 281 ax_name, unit_list, length = TNAME, TUNIT, None 282 243 283 if mmath == xr : 244 for Name in axName : 245 try : 246 ix = tab.dims.index (Name) 247 ax = Name 248 except : pass 249 250 for i, dim in enumerate (tab.dims) : 251 if 'units' in tab.coords[dim].attrs.keys() : 252 for name in unList : 253 if name in tab.coords[dim].attrs['units'] : 254 ix = i ; ax = dim 255 else : 256 #if axis in xName : ix=-1 257 #if axis in yName : 258 # if len(tab.shape) >= 2 : ix=-2 259 #if axis in zName : 260 # if len(tab.shape) >= 3 : ix=-3 261 #if axis in tName : 262 # if len(tab.shape) >=3 : ix=-3 263 # if len(tab.shape) >=4 : ix=-4 264 265 l_shape = tab.shape 266 for nn in np.arange ( len(l_shape)) : 267 if l_shape[nn] in Length : ix = nn 268 269 return ix, ax 270 271 def findAxis ( tab, axis= 'z' ) : 272 ix, xx = __findAxis__ (tab, axis) 273 return xx 274 275 def fixed_lon (lon, center_lon=0.0) : 276 ''' 277 Returns corrected longitudes for nicer plots 284 # Try by name 285 for dim in ax_name : 286 if dim in ptab.dims : 287 ix, ax = ptab.dims.index (dim), dim 288 289 # If not found, try by axis attributes 290 if not ix : 291 for i, dim in enumerate (ptab.dims) : 292 if 'axis' in ptab.coords[dim].attrs.keys() : 293 l_axis = ptab.coords[dim].attrs['axis'] 294 if axis in ax_name and l_axis == 'X' : 295 ix, ax = (i, dim) 296 if axis in ax_name and l_axis == 'Y' : 297 ix, ax = (i, dim) 298 if axis in ax_name and l_axis == 'Z' : 299 ix, ax = (i, dim) 300 if axis in ax_name and l_axis == 'T' : 301 ix, ax = (i, dim) 302 303 # If not found, try by units 304 if not ix : 305 for i, dim in enumerate (ptab.dims) : 306 if 'units' in ptab.coords[dim].attrs.keys() : 307 for name in unit_list : 308 if name in ptab.coords[dim].attrs['units'] : 309 ix, ax = i, dim 310 311 # If numpy array or dimension not found, try by length 312 if mmath != xr or not ix : 313 if length : 314 l_shape = ptab.shape 315 for nn in np.arange ( len(l_shape) ) : 316 if l_shape[nn] in length : 317 ix = nn 318 319 if ix and back : 320 ix -= len(ptab.shape) 321 322 return ax, ix 323 324 def find_axis ( ptab, axis='z', back=True ) : 325 '''Version of find_axis with no __''' 326 ix, xx = __find_axis__ (ptab, axis, back) 327 return xx, ix 328 329 def fixed_lon (plon, center_lon=0.0) : 330 '''Returns corrected longitudes for nicer plots 278 331 279 332 lon : longitudes of the grid. At least 2D. 280 333 center_lon : center longitude. Default=0. 281 334 282 Designed by Phil Pelson. See https://gist.github.com/pelson/79cf31ef324774c97ae7 283 ''' 284 mmath = __mmath__ (lon) 335 Designed by Phil Pelson. 336 See https://gist.github.com/pelson/79cf31ef324774c97ae7 337 ''' 338 mmath = __mmath__ (plon) 339 340 f_lon = plon.copy () 341 342 f_lon = mmath.where (f_lon > center_lon+180., f_lon-360.0, f_lon) 343 f_lon = mmath.where (f_lon < center_lon-180., f_lon+360.0, f_lon) 344 345 for i, start in enumerate (np.argmax (np.abs (np.diff (f_lon, axis=-1)) > 180., axis=-1)) : 346 f_lon [..., i, start+1:] += 360. 347 348 # Special case for eORCA025 349 if f_lon.shape [-1] == 1442 : 350 f_lon [..., -2, :] = f_lon [..., -3, :] 351 if f_lon.shape [-1] == 1440 : 352 f_lon [..., -1, :] = f_lon [..., -2, :] 353 354 if f_lon.min () > center_lon : 355 f_lon += -360.0 356 if f_lon.max () < center_lon : 357 f_lon += 360.0 358 359 if f_lon.min () < center_lon-360.0 : 360 f_lon += 360.0 361 if f_lon.max () > center_lon+360.0 : 362 f_lon += -360.0 363 364 return f_lon 365 366 def bounds_clolon ( pbounds_lon, plon, rad=False, deg=True) : 367 '''Choose closest to lon0 longitude, adding/substacting 360° if needed 368 ''' 369 370 if rad : 371 lon_range = 2.0*np.pi 372 if deg : 373 lon_range = 360.0 374 b_clolon = pbounds_lon.copy () 375 376 b_clolon = xr.where ( b_clolon < plon-lon_range/2., 377 b_clolon+lon_range, 378 b_clolon ) 379 b_clolon = xr.where ( b_clolon > plon+lon_range/2., 380 b_clolon-lon_range, 381 b_clolon ) 382 return b_clolon 383 384 def unify_dims ( dd, x='x', y='y', z='olevel', t='time_counter', verbose=False ) : 385 '''Rename dimensions to unify them between NEMO versions 386 ''' 387 for xx in XNAME : 388 if xx in dd.dims and xx != x : 389 if verbose : 390 print ( f"{xx} renamed to {x}" ) 391 dd = dd.rename ( {xx:x}) 392 393 for yy in YNAME : 394 if yy in dd.dims and yy != y : 395 if verbose : 396 print ( f"{yy} renamed to {y}" ) 397 dd = dd.rename ( {yy:y} ) 398 399 for zz in ZNAME : 400 if zz in dd.dims and zz != z : 401 if verbose : 402 print ( f"{zz} renamed to {z}" ) 403 dd = dd.rename ( {zz:z} ) 404 405 for tt in TNAME : 406 if tt in dd.dims and tt != t : 407 if verbose : 408 print ( f"{tt} renamed to {t}" ) 409 dd = dd.rename ( {tt:t} ) 410 411 return dd 412 413 414 if SimpleImputer : 415 def fill_empty (ptab, sval=np.nan, transpose=False) : 416 '''Fill empty values 417 418 Useful when NEMO has run with no wet points options : 419 some parts of the domain, with no ocean points, have no 420 values 421 ''' 422 mmath = __mmath__ (ptab) 423 424 imp = SimpleImputer (missing_values=sval, strategy='mean') 425 if transpose : 426 imp.fit (ptab.T) 427 ztab = imp.transform (ptab.T).T 428 else : 429 imp.fit (ptab) 430 ztab = imp.transform (ptab) 431 432 if mmath == xr : 433 ztab = xr.DataArray (ztab, dims=ztab.dims, coords=ztab.coords) 434 ztab.attrs.update (ptab.attrs) 435 436 return ztab 437 285 438 286 fixed_lon = lon.copy () 287 288 fixed_lon = mmath.where (fixed_lon > center_lon+180., fixed_lon-360.0, fixed_lon) 289 fixed_lon = mmath.where (fixed_lon < center_lon-180., fixed_lon+360.0, fixed_lon) 290 291 for i, start in enumerate (np.argmax (np.abs (np.diff (fixed_lon, axis=-1)) > 180., axis=-1)) : 292 fixed_lon [..., i, start+1:] += 360. 293 294 # Special case for eORCA025 295 if fixed_lon.shape [-1] == 1442 : fixed_lon [..., -2, :] = fixed_lon [..., -3, :] 296 if fixed_lon.shape [-1] == 1440 : fixed_lon [..., -1, :] = fixed_lon [..., -2, :] 297 298 if fixed_lon.min () > center_lon : fixed_lon += -360.0 299 if fixed_lon.max () < center_lon : fixed_lon += 360.0 300 301 if fixed_lon.min () < center_lon-360.0 : fixed_lon += 360.0 302 if fixed_lon.max () > center_lon+360.0 : fixed_lon += -360.0 303 304 return fixed_lon 305 306 def bounds_clolon ( bounds_lon, lon, rad=False, deg=True) : 307 '''Choose closest to lon0 longitude, adding or substacting 360° if needed''' 308 309 if rad : lon_range = 2.0*np.pi 310 if deg : lon_range = 360.0 311 bounds_clolon = bounds_lon.copy () 312 313 bounds_clolon = xr.where ( bounds_clolon < lon-lon_range/2., bounds_clolon+lon_range, bounds_clolon ) 314 bounds_clolon = xr.where ( bounds_clolon > lon+lon_range/2., bounds_clolon-lon_range, bounds_clolon ) 315 316 return bounds_clolon 317 318 def UnifyDims ( dd, udims=dim_names, verbose=False ) : 319 ''' 320 Rename dimensions to unify them between NEMO versions 321 ''' 322 323 if udims['x'] : 324 for xx in xName : 325 if xx in dd.dims and xx != udims['x'] : 326 if verbose : print ( f"{xx} renamed to {udims['x']}" ) 327 dd = dd.rename ( {xx:udims['x']}) 328 if udims['y'] : 329 for yy in yName : 330 if yy in dd.dims and yy != udims['y'] : 331 if verbose : print ( f"{yy} renamed to {udims['y']}" ) 332 dd = dd.rename ( {yy:udims['y']} ) 333 if udims['z'] : 334 for zz in zName : 335 if zz in dd.dims and zz != udims['z'] : 336 if verbose : print ( f"{zz} renamed to {udims['z']}" ) 337 dd = dd.rename ( {zz:udims['z']} ) 338 if udims['t'] : 339 for tt in tName : 340 if tt in dd.dims and tt != udims['t'] : 341 if verbose : print ( f"{tt} renamed to {udims['t']}" ) 342 dd = dd.rename ( {tt:udims['t']} ) 343 344 return dd 345 346 def fill_empty (ztab, sval=np.nan, transpose=False) : 347 ''' 348 Fill values 349 350 Useful when NEMO has run with no wet points options : 351 some parts of the domain, with no ocean points, have no 352 values 353 ''' 354 from sklearn.impute import SimpleImputer 355 mmath = __mmath__ (ztab) 356 357 imp = SimpleImputer (missing_values=sval, strategy='mean') 358 if transpose : 359 imp.fit (ztab.T) 360 ptab = imp.transform (ztab.T).T 361 else : 362 imp.fit (ztab) 363 ptab = imp.transform (ztab) 364 365 if mmath == xr : 366 ptab = xr.DataArray (ptab, dims=ztab.dims, coords=ztab.coords) 367 ptab.attrs = ztab.attrs 368 369 return ptab 370 371 def fill_lonlat (lon, lat, sval=-1) : 372 ''' 373 Fill longitude/latitude values 374 375 Useful when NEMO has run with no wet points options : 439 else : 440 print ("Import error of sklearn.impute.SimpleImputer") 441 def fill_empty (ptab, sval=np.nan, transpose=False) : 442 '''Void version of fill_empy, because module sklearn.impute.SimpleImputer is not available 443 444 fill_empty : 445 Fill values 446 447 Useful when NEMO has run with no wet points options : 448 some parts of the domain, with no ocean points, have no 449 values 450 ''' 451 print ( 'Error : module sklearn.impute.SimpleImputer not found' ) 452 print ( 'Can not call fill_empty' ) 453 print ( 'Call arguments where : ' ) 454 print ( f'{ptab.shape=} {sval=} {transpose=}' ) 455 456 def fill_lonlat (plon, plat, sval=-1) : 457 '''Fill longitude/latitude values 458 459 Useful when NEMO has run with no wet points options : 376 460 some parts of the domain, with no ocean points, have no 377 461 lon/lat values 378 462 ''' 379 463 from sklearn.impute import SimpleImputer 380 mmath = __mmath__ ( lon)464 mmath = __mmath__ (plon) 381 465 382 466 imp = SimpleImputer (missing_values=sval, strategy='mean') 383 imp.fit ( lon)384 plon = imp.transform (lon)385 imp.fit ( lat.T)386 plat = imp.transform (lat.T).T467 imp.fit (plon) 468 zlon = imp.transform (plon) 469 imp.fit (plat.T) 470 zlat = imp.transform (plat.T).T 387 471 388 472 if mmath == xr : 389 plon = xr.DataArray (plon, dims=lon.dims, coords=lon.coords)390 plat = xr.DataArray (plat, dims=lat.dims, coords=lat.coords)391 plon.attrs = lon.attrs ; plat.attrs = lat.attrs392 393 plon = fixed_lon (plon) 394 395 return plon, plat 396 397 def fill_bounds_lonlat (bounds_lon, bounds_lat, sval=-1) : 398 ''' 399 Fill longitude/latitude bounds values400 401 Useful when NEMO has run with no wet points options : 473 zlon = xr.DataArray (zlon, dims=plon.dims, coords=plon.coords) 474 zlat = xr.DataArray (zlat, dims=plat.dims, coords=plat.coords) 475 zlon.attrs.update (plon.attrs) 476 zlat.attrs.update (plat.attrs) 477 478 zlon = fixed_lon (zlon) 479 480 return zlon, zlat 481 482 def fill_bounds_lonlat (pbounds_lon, pbounds_lat, sval=-1) : 483 '''Fill longitude/latitude bounds values 484 485 Useful when NEMO has run with no wet points options : 402 486 some parts of the domain, with no ocean points, as no 403 487 lon/lat values 404 488 ''' 405 mmath = __mmath__ ( bounds_lon)406 407 p_bounds_lon = np.empty (bounds_lon.shape )408 p_bounds_lat = np.empty (bounds_lat.shape )489 mmath = __mmath__ (pbounds_lon) 490 491 z_bounds_lon = np.empty ( pbounds_lon.shape ) 492 z_bounds_lat = np.empty ( pbounds_lat.shape ) 409 493 410 494 imp = SimpleImputer (missing_values=sval, strategy='mean') 411 412 for n in np.arange (4) : 413 imp.fit ( bounds_lon[:,:,n])414 p_bounds_lon[:,:,n] = imp.transform (bounds_lon[:,:,n])415 imp.fit ( bounds_lat[:,:,n].T)416 p_bounds_lat[:,:,n] = imp.transform (bounds_lat[:,:,n].T).T417 495 496 for n in np.arange (4) : 497 imp.fit (pbounds_lon[:,:,n]) 498 z_bounds_lon[:,:,n] = imp.transform (pbounds_lon[:,:,n]) 499 imp.fit (pbounds_lat[:,:,n].T) 500 z_bounds_lat[:,:,n] = imp.transform (pbounds_lat[:,:,n].T).T 501 418 502 if mmath == xr : 419 p_bounds_lon = xr.DataArray (bounds_lon, dims=bounds_lon.dims, coords=bounds_lon.coords) 420 p_bounds_lat = xr.DataArray (bounds_lat, dims=bounds_lat.dims, coords=bounds_lat.coords) 421 p_bounds_lon.attrs = bounds_lat.attrs ; p_bounds_lat.attrs = bounds_lat.attrs 422 423 return p_bounds_lon, p_bounds_lat 424 425 def jeq (lat) : 426 ''' 427 Returns j index of equator in the grid 428 503 z_bounds_lon = xr.DataArray (pbounds_lon, dims=pbounds_lon.dims, 504 coords=pbounds_lon.coords) 505 z_bounds_lat = xr.DataArray (pbounds_lat, dims=pbounds_lat.dims, 506 coords=pbounds_lat.coords) 507 z_bounds_lon.attrs.update (pbounds_lat.attrs) 508 z_bounds_lat.attrs.update (pbounds_lat.attrs) 509 510 return z_bounds_lon, z_bounds_lat 511 512 def jeq (plat) : 513 '''Returns j index of equator in the grid 514 429 515 lat : latitudes of the grid. At least 2D. 430 516 ''' 431 mmath = __mmath__ (lat) 432 ix, ax = __findAxis__ (lat, 'x') 433 jy, ay = __findAxis__ (lat, 'y') 517 mmath = __mmath__ (plat) 518 jy = __find_axis__ (plat, 'y')[-1] 434 519 435 520 if mmath == xr : 436 jeq = int ( np.mean ( np.argmin (np.abs (np.float64 (lat)), axis=jy) ) ) 437 else : 438 jeq = np.argmin (np.abs (np.float64 (lat[...,:, 0]))) 439 return jeq 440 441 def lon1D (lon, lat=None) : 442 ''' 443 Returns 1D longitude for simple plots. 444 445 lon : longitudes of the grid 446 lat (optionnal) : latitudes of the grid 447 ''' 448 mmath = __mmath__ (lon) 449 jpj, jpi = lon.shape [-2:] 450 if np.max (lat) : 451 je = jeq (lat) 452 #lon1D = lon.copy() [..., je, :] 453 lon0 = lon [..., je, 0].copy() 454 dlon = lon [..., je, 1].copy() - lon [..., je, 0].copy() 455 lon1D = np.linspace ( start=lon0, stop=lon0+360.+2*dlon, num=jpi ) 521 jj = int ( np.mean ( np.argmin (np.abs (np.float64 (plat)), 522 axis=jy) ) ) 456 523 else : 457 lon0 = lon [..., jpj//3, 0].copy() 458 dlon = lon [..., jpj//3, 1].copy() - lon [..., jpj//3, 0].copy() 459 lon1D = np.linspace ( start=lon0, stop=lon0+360.+2*dlon, num=jpi ) 524 jj = np.argmin (np.abs (np.float64 (plat[...,:, 0]))) 525 526 return jj 527 528 def lon1d (plon, plat=None) : 529 '''Returns 1D longitude for simple plots. 530 531 plon : longitudes of the grid 532 plat (optionnal) : latitudes of the grid 533 ''' 534 mmath = __mmath__ (plon) 535 jpj, jpi = plon.shape [-2:] 536 if np.max (plat) : 537 je = jeq (plat) 538 lon0 = plon [..., je, 0].copy() 539 dlon = plon [..., je, 1].copy() - plon [..., je, 0].copy() 540 lon_1d = np.linspace ( start=lon0, stop=lon0+360.+2*dlon, num=jpi ) 541 else : 542 lon0 = plon [..., jpj//3, 0].copy() 543 dlon = plon [..., jpj//3, 1].copy() - plon [..., jpj//3, 0].copy() 544 lon_1d = np.linspace ( start=lon0, stop=lon0+360.+2*dlon, num=jpi ) 460 545 461 546 #start = np.argmax (np.abs (np.diff (lon1D, axis=-1)) > 180.0, axis=-1) … … 463 548 464 549 if mmath == xr : 465 lon 1D = xr.DataArray( lon1D, dims=('lon',), coords=(lon1D,))466 lon 1D.attrs = lon.attrs467 lon 1D.attrs['units'] = 'degrees_east'468 lon 1D.attrs['standard_name'] = 'longitude'469 lon 1D.attrs['long_name :'] = 'Longitude'470 471 return lon 1D472 473 def latreg ( lat, diff=0.1) :474 ''' 475 Returns maximum j index where gridlines are along latitudesin the northern hemisphere476 550 lon_1d = xr.DataArray( lon_1d, dims=('lon',), coords=(lon_1d,)) 551 lon_1d.attrs.update (plon.attrs) 552 lon_1d.attrs['units'] = 'degrees_east' 553 lon_1d.attrs['standard_name'] = 'longitude' 554 lon_1d.attrs['long_name :'] = 'Longitude' 555 556 return lon_1d 557 558 def latreg (plat, diff=0.1) : 559 '''Returns maximum j index where gridlines are along latitudes 560 in the northern hemisphere 561 477 562 lat : latitudes of the grid (2D) 478 563 diff [optional] : tolerance 479 564 ''' 480 mmath = __mmath__ (lat) 481 if diff == None : 482 dy = np.float64 (np.mean (np.abs (lat - np.roll (lat,shift=1,axis=-2, roll_coords=False)))) 565 #mmath = __mmath__ (plat) 566 if diff is None : 567 dy = np.float64 (np.mean (np.abs (plat - 568 np.roll (plat,shift=1,axis=-2, roll_coords=False)))) 483 569 print ( f'{dy=}' ) 484 570 diff = dy/100. 485 571 486 je = jeq ( lat)487 jreg = np.where ( lat[...,je:,:].max(axis=-1) - lat[...,je:,:].min(axis=-1)< diff)[-1][-1] + je488 latreg = np.float64 (lat[...,jreg,:].mean(axis=-1))489 JREG = jreg490 491 return jreg, la treg492 493 def lat1 D (lat) :494 ''' 495 Returns 1D latitudes for zonal means and simple plots. 496 497 lat : latitudes of the grid (2D)498 '''499 mmath = __mmath__ (lat)500 jpj , jpi = lat.shape[-2:]501 502 dy = np.float64 (np.mean (np.abs ( lat - np.roll (lat, shift=1,axis=-2))))503 je = jeq ( lat)504 lat_eq = np.float64 ( lat[...,je,:].mean(axis=-1))505 506 jreg, lat_reg = latreg ( lat)507 lat_ave = np.mean ( lat, axis=-1)508 509 #print ( f'{dy=} {jpj=} {je=} {lat_eq=} {jreg=} ' )510 511 if (np.abs (lat_eq) < dy/100.) : # T, U or W grid512 if jpj-1 > jreg : dys = (90.-lat_reg) / (jpj-jreg-1)*0.5513 else :dys = (90.-lat_reg) / 2.0514 yrange = (90.-dys-lat_reg)572 je = jeq (plat) 573 jreg = np.where (plat[...,je:,:].max(axis=-1) - 574 plat[...,je:,:].min(axis=-1)< diff)[-1][-1] + je 575 lareg = np.float64 (plat[...,jreg,:].mean(axis=-1)) 576 577 return jreg, lareg 578 579 def lat1d (plat) : 580 '''Returns 1D latitudes for zonal means and simple plots. 581 582 plat : latitudes of the grid (2D) 583 ''' 584 mmath = __mmath__ (plat) 585 iy = __find_axis__ (plat, 'y')[-1] 586 jpj = plat.shape[iy] 587 588 dy = np.float64 (np.mean (np.abs (plat - np.roll (plat, shift=1,axis=-2)))) 589 je = jeq (plat) 590 lat_eq = np.float64 (plat[...,je,:].mean(axis=-1)) 591 592 jreg, lat_reg = latreg (plat) 593 lat_ave = np.mean (plat, axis=-1) 594 595 if np.abs (lat_eq) < dy/100. : # T, U or W grid 596 if jpj-1 > jreg : 597 dys = (90.-lat_reg) / (jpj-jreg-1)*0.5 598 else : 599 dys = (90.-lat_reg) / 2.0 600 yrange = 90.-dys-lat_reg 515 601 else : # V or F grid 516 602 yrange = 90.-lat_reg 517 603 518 604 if jpj-1 > jreg : 519 lat1D = mmath.where (lat_ave<lat_reg, lat_ave, lat_reg + yrange * (np.arange(jpj)-jreg)/(jpj-jreg-1) ) 605 lat_1d = mmath.where (lat_ave<lat_reg, 606 lat_ave, 607 lat_reg + yrange * (np.arange(jpj)-jreg)/(jpj-jreg-1) ) 520 608 else : 521 lat 1D= lat_ave522 lat 1D[-1] = 90.0609 lat_1d = lat_ave 610 lat_1d[-1] = 90.0 523 611 524 612 if mmath == xr : 525 lat1D = xr.DataArray( lat1D.values, dims=('lat',), coords=(lat1D,)) 526 lat1D.attrs = lat.attrs 527 lat1D.attrs ['units'] = 'degrees_north' 528 lat1D.attrs ['standard_name'] = 'latitude' 529 lat1D.attrs ['long_name :'] = 'Latitude' 530 531 return lat1D 532 533 def latlon1D (lat, lon) : 534 ''' 535 Returns simple latitude and longitude (1D) for simple plots. 536 537 lat, lon : latitudes and longitudes of the grid (2D) 538 ''' 539 return lat1D (lat), lon1D (lon, lat) 613 lat_1d = xr.DataArray( lat_1d.values, dims=('lat',), coords=(lat_1d,)) 614 lat_1d.attrs.update (plat.attrs) 615 lat_1d.attrs ['units'] = 'degrees_north' 616 lat_1d.attrs ['standard_name'] = 'latitude' 617 lat_1d.attrs ['long_name :'] = 'Latitude' 618 619 return lat_1d 620 621 def latlon1d (plat, plon) : 622 '''Returns simple latitude and longitude (1D) for simple plots. 623 624 plat, plon : latitudes and longitudes of the grid (2D) 625 ''' 626 return lat1d (plat), lon1d (plon, plat) 627 628 def ff (plat) : 629 '''Returns Coriolis factor 630 ''' 631 zff = np.sin (RAD * plat) * OMEGA 632 return zff 633 634 def beta (plat) : 635 '''Return Beta factor (derivative of Coriolis factor) 636 ''' 637 zbeta = np.cos (RAD * plat) * OMEGA / RA 638 return zbeta 540 639 541 640 def mask_lonlat (ptab, x0, x1, y0, y1, lon, lat, sval=np.nan) : 641 '''Returns masked values outside a lat/lon box 642 ''' 542 643 mmath = __mmath__ (ptab) 543 try:644 if mmath == xr : 544 645 lon = lon.copy().to_masked_array() 545 646 lat = lat.copy().to_masked_array() 546 except : pass 547 548 mask = np.logical_and (np.logical_and(lat>y0, lat<y1), 549 np.logical_or (np.logical_or (np.logical_and(lon>x0, lon<x1), np.logical_and(lon+360>x0, lon+360<x1)), 550 np.logical_and(lon-360>x0, lon-360<x1))) 551 tab = mmath.where (mask, ptab, np.nan) 552 647 648 mask = np.logical_and (np.logical_and(lat>y0, lat<y1), 649 np.logical_or (np.logical_or ( 650 np.logical_and(lon>x0, lon<x1), 651 np.logical_and(lon+360>x0, lon+360<x1)), 652 np.logical_and(lon-360>x0, lon-360<x1))) 653 tab = mmath.where (mask, ptab, sval) 654 553 655 return tab 554 656 555 def extend (tab, Lon=False, jplus=25, jpi=None, nperio=4) : 556 ''' 557 Returns extended field eastward to have better plots, and box average crossing the boundary 657 def extend (ptab, blon=False, jplus=25, jpi=None, nperio=4) : 658 '''Returns extended field eastward to have better plots, 659 and box average crossing the boundary 660 558 661 Works only for xarray and numpy data (?) 559 560 Useful for vertical sections in OCE and ATM. 561 562 tab : field to extend. 563 Lon : (optional, default=False) : if True, add 360 in the extended parts of the field 564 jpi : normal longitude dimension of the field. exrtend does nothing it the actual 565 size of the field != jpi (avoid to extend several times) 566 jplus (optional, default=25) : number of points added on the east side of the field 567 568 ''' 569 mmath = __mmath__ (tab) 570 571 if tab.shape[-1] == 1 : extend = tab 662 Useful for plotting vertical sections in OCE and ATM. 663 664 ptab : field to extend. 665 blon : (optional, default=False) : if True, add 360 in the extended 666 parts of the field 667 jpi : normal longitude dimension of the field. extend does nothing 668 if the actual size of the field != jpi 669 (avoid to extend several times in notebooks) 670 jplus (optional, default=25) : number of points added on 671 the east side of the field 672 673 ''' 674 mmath = __mmath__ (ptab) 675 676 if ptab.shape[-1] == 1 : 677 tabex = ptab 572 678 573 679 else : 574 if jpi == None : jpi = tab.shape[-1] 575 576 if Lon : xplus = -360.0 577 else : xplus = 0.0 578 579 if tab.shape[-1] > jpi : 580 extend = tab 680 if jpi is None : 681 jpi = ptab.shape[-1] 682 683 if blon : 684 xplus = -360.0 685 else : 686 xplus = 0.0 687 688 if ptab.shape[-1] > jpi : 689 tabex = ptab 581 690 else : 582 if nperio == 0 or nperio == 4.2:583 istart = 0 ; le=jpi+1 ; la=0691 if nperio in [ 0, 4.2 ] : 692 istart, le, la = 0, jpi+1, 0 584 693 if nperio == 1 : 585 istart = 0 ; le=jpi+1 ; la=0586 if nperio == 4 or nperio == 6: # OPA case with two halo points for periodicity587 istart = 1 ; le=jpi-2 ; la=1# Perfect, except at the pole that should be masked by lbc_plot588 694 istart, le, la = 0, jpi+1, 0 695 if nperio in [4, 6] : # OPA case with two halo points for periodicity 696 # Perfect, except at the pole that should be masked by lbc_plot 697 istart, le, la = 1, jpi-2, 1 589 698 if mmath == xr : 590 extend = np.concatenate ((tab.values[..., istart :istart+le+1 ] + xplus, 591 tab.values[..., istart+la:istart+la+jplus] ), axis=-1) 592 lon = tab.dims[-1] 699 tabex = np.concatenate ( 700 (ptab.values[..., istart :istart+le+1 ] + xplus, 701 ptab.values[..., istart+la:istart+la+jplus] ), 702 axis=-1) 703 lon = ptab.dims[-1] 593 704 new_coords = [] 594 for coord in tab.dims : 595 if coord == lon : new_coords.append ( np.arange( extend.shape[-1])) 596 else : new_coords.append ( tab.coords[coord].values) 597 extend = xr.DataArray ( extend, dims=tab.dims, coords=new_coords ) 598 else : 599 extend = np.concatenate ((tab [..., istart :istart+le+1 ] + xplus, 600 tab [..., istart+la:istart+la+jplus] ), axis=-1) 601 return extend 602 603 def orca2reg (ff, lat_name='nav_lat', lon_name='nav_lon', y_name='y', x_name='x') : 604 ''' 605 Assign an ORCA dataset on a regular grid. 705 for coord in ptab.dims : 706 if coord == lon : 707 new_coords.append ( np.arange( tabex.shape[-1])) 708 else : 709 new_coords.append ( ptab.coords[coord].values) 710 tabex = xr.DataArray ( tabex, dims=ptab.dims, 711 coords=new_coords ) 712 else : 713 tabex = np.concatenate ( 714 (ptab [..., istart :istart+le+1 ] + xplus, 715 ptab [..., istart+la:istart+la+jplus] ), 716 axis=-1) 717 return tabex 718 719 def orca2reg (dd, lat_name='nav_lat', lon_name='nav_lon', 720 y_name='y', x_name='x') : 721 '''Assign an ORCA dataset on a regular grid. 722 606 723 For use in the tropical region. 607 608 Inputs : 724 Inputs : 609 725 ff : xarray dataset 610 726 lat_name, lon_name : name of latitude and longitude 2D field in ff 611 727 y_name, x_name : namex of dimensions in ff 612 728 613 729 Returns : xarray dataset with rectangular grid. Incorrect above 20°N 614 730 ''' 615 731 # Compute 1D longitude and latitude 616 (lat, lon) = latlon1D (ff[lat_name], ff[lon_name]) 617 732 (zlat, zlon) = latlon1d ( dd[lat_name], dd[lon_name]) 733 734 zdd = dd 618 735 # Assign lon and lat as dimensions of the dataset 619 if y_name in ff.dims :620 lat = xr.DataArray (lat, coords=[lat,], dims=['lat',])621 ff = ff.rename_dims ({y_name: "lat",}).assign_coords (lat=lat)622 if x_name in ff.dims :623 lon = xr.DataArray (lon, coords=[lon,], dims=['lon',])624 ff = ff.rename_dims ({x_name: "lon",}).assign_coords (lon=lon)736 if y_name in zdd.dims : 737 zlat = xr.DataArray (zlat, coords=[zlat,], dims=['lat',]) 738 zdd = zdd.rename_dims ({y_name: "lat",}).assign_coords (lat=zlat) 739 if x_name in zdd.dims : 740 zlon = xr.DataArray (zlon, coords=[zlon,], dims=['lon',]) 741 zdd = zdd.rename_dims ({x_name: "lon",}).assign_coords (lon=zlon) 625 742 # Force dimensions to be in the right order 626 743 coord_order = ['lat', 'lon'] 627 744 for dim in [ 'depthw', 'depthv', 'depthu', 'deptht', 'depth', 'z', 628 'time_counter', 'time', 'tbnds', 745 'time_counter', 'time', 'tbnds', 629 746 'bnds', 'axis_nbounds', 'two2', 'two1', 'two', 'four',] : 630 if dim in ff.dims : coord_order.insert (0, dim) 631 632 ff = ff.transpose (*coord_order) 633 return ff 747 if dim in zdd.dims : 748 coord_order.insert (0, dim) 749 750 zdd = zdd.transpose (*coord_order) 751 return zdd 634 752 635 753 def lbc_init (ptab, nperio=None) : 636 ''' 637 Prepare for all lbc calls 638 754 '''Prepare for all lbc calls 755 639 756 Set periodicity on input field 640 757 nperio : Type of periodicity … … 648 765 See NEMO documentation for further details 649 766 ''' 650 jpi = None ; jpj = None 651 ix, ax = __findAxis__ (ptab, 'x') 652 jy, ay = __findAxis__ (ptab, 'y') 653 if ax : jpi = ptab.shape[ix] 654 if ay : jpj = ptab.shape[jy] 655 656 if nperio == None : nperio = __guessNperio__ (jpj, jpi, nperio) 657 658 if nperio not in nperio_valid_range : 659 raise Exception ( f'{nperio=} is not in the valid range {nperio_valid_range}' ) 767 jpi, jpj = None, None 768 ax, ix = __find_axis__ (ptab, 'x') 769 ay, jy = __find_axis__ (ptab, 'y') 770 if ax : 771 jpi = ptab.shape[ix] 772 if ay : 773 jpj = ptab.shape[jy] 774 775 if nperio is None : 776 nperio = __guess_nperio__ (jpj, jpi, nperio) 777 778 if nperio not in NPERIO_VALID_RANGE : 779 raise AttributeError ( f'{nperio=} is not in the valid range {NPERIO_VALID_RANGE}' ) 660 780 661 781 return jpj, jpi, nperio 662 663 def lbc (ptab, nperio=None, cd_type='T', psgn=1.0, nemo_4 U_bug=False) :664 ''' 665 Set periodicity on input field 782 783 def lbc (ptab, nperio=None, cd_type='T', psgn=1.0, nemo_4u_bug=False) : 784 '''Set periodicity on input field 785 666 786 ptab : Input array (works for rank 2 at least : ptab[...., lat, lon]) 667 787 nperio : Type of periodicity 668 788 cd_type : Grid specification : T, U, V or F 669 789 psgn : For change of sign for vector components (1 for scalars, -1 for vector components) 670 790 671 791 See NEMO documentation for further details 672 792 ''' 673 jp j, jpi, nperio = lbc_init (ptab, nperio)674 ix, ax = __findAxis__ (ptab, 'x')675 jy, ay = __findAxis__ (ptab, 'y')793 jpi, nperio = lbc_init (ptab, nperio)[1:] 794 ax = __find_axis__ (ptab, 'x')[0] 795 ay = __find_axis__ (ptab, 'y')[0] 676 796 psgn = ptab.dtype.type (psgn) 677 mmath = __mmath__ (ptab) 678 679 if mmath == xr : ztab = ptab.values.copy () 680 else : ztab = ptab.copy () 681 682 if ax : 797 mmath = __mmath__ (ptab) 798 799 if mmath == xr : 800 ztab = ptab.values.copy () 801 else : 802 ztab = ptab.copy () 803 804 if ax : 683 805 # 684 806 #> East-West boundary conditions … … 689 811 ztab [..., -1] = ztab [..., 1] 690 812 691 if ay : 813 if ay : 692 814 # 693 815 #> North-South boundary conditions … … 698 820 ztab [..., -1, 0 ] = psgn * ztab [..., -3, 2 ] 699 821 ztab [..., -2, jpi//2: ] = psgn * ztab [..., -2, jpi//2:0:-1 ] 700 822 701 823 if cd_type == 'U' : 702 ztab [..., -1, 0:-1 ] = psgn * ztab [..., -3, -1:0:-1 ] 824 ztab [..., -1, 0:-1 ] = psgn * ztab [..., -3, -1:0:-1 ] 703 825 ztab [..., -1, 0 ] = psgn * ztab [..., -3, 1 ] 704 826 ztab [..., -1, -1 ] = psgn * ztab [..., -3, -2 ] 705 706 if nemo_4 U_bug :827 828 if nemo_4u_bug : 707 829 ztab [..., -2, jpi//2+1:-1] = psgn * ztab [..., -2, jpi//2-2:0:-1] 708 830 ztab [..., -2, jpi//2-1 ] = psgn * ztab [..., -2, jpi//2 ] 709 831 else : 710 832 ztab [..., -2, jpi//2-1:-1] = psgn * ztab [..., -2, jpi//2:0:-1] 711 712 if cd_type == 'V' : 833 834 if cd_type == 'V' : 713 835 ztab [..., -2, 1: ] = psgn * ztab [..., -3, jpi-1:0:-1 ] 714 ztab [..., -1, 1: ] = psgn * ztab [..., -4, -1:0:-1 ] 836 ztab [..., -1, 1: ] = psgn * ztab [..., -4, -1:0:-1 ] 715 837 ztab [..., -1, 0 ] = psgn * ztab [..., -4, 2 ] 716 838 717 839 if cd_type == 'F' : 718 840 ztab [..., -2, 0:-1 ] = psgn * ztab [..., -3, -1:0:-1 ] … … 720 842 ztab [..., -1, 0 ] = psgn * ztab [..., -4, 1 ] 721 843 ztab [..., -1, -1 ] = psgn * ztab [..., -4, -2 ] 722 844 723 845 if nperio in [4.2] : # North fold T-point pivot 724 846 if cd_type in [ 'T', 'W' ] : # T-, W-point 725 847 ztab [..., -1, jpi//2: ] = psgn * ztab [..., -1, jpi//2:0:-1 ] 726 848 727 849 if cd_type == 'U' : 728 850 ztab [..., -1, jpi//2-1:-1] = psgn * ztab [..., -1, jpi//2:0:-1] 729 730 if cd_type == 'V' : 851 852 if cd_type == 'V' : 731 853 ztab [..., -1, 1: ] = psgn * ztab [..., -2, jpi-1:0:-1 ] 732 854 733 855 if cd_type == 'F' : 734 856 ztab [..., -1, 0:-1 ] = psgn * ztab [..., -2, -1:0:-1 ] 735 736 if nperio in [5, 6] : # North fold F-point pivot 857 858 if nperio in [5, 6] : # North fold F-point pivot 737 859 if cd_type in ['T', 'W'] : 738 860 ztab [..., -1, 0: ] = psgn * ztab [..., -2, -1::-1 ] 739 861 740 862 if cd_type == 'U' : 741 ztab [..., -1, 0:-1 ] = psgn * ztab [..., -2, -2::-1 ] 863 ztab [..., -1, 0:-1 ] = psgn * ztab [..., -2, -2::-1 ] 742 864 ztab [..., -1, -1 ] = psgn * ztab [..., -2, 0 ] # Bug ? 743 865 744 866 if cd_type == 'V' : 745 867 ztab [..., -1, 0: ] = psgn * ztab [..., -3, -1::-1 ] 746 868 ztab [..., -2, jpi//2: ] = psgn * ztab [..., -2, jpi//2-1::-1 ] 747 869 748 870 if cd_type == 'F' : 749 871 ztab [..., -1, 0:-1 ] = psgn * ztab [..., -3, -2::-1 ] 750 872 ztab [..., -1, -1 ] = psgn * ztab [..., -3, 0 ] 751 873 ztab [..., -2, jpi//2:-1] = psgn * ztab [..., -2, jpi//2-2::-1 ] 752 874 753 875 # 754 876 #> East-West boundary conditions … … 762 884 ztab = xr.DataArray ( ztab, dims=ptab.dims, coords=ptab.coords ) 763 885 ztab.attrs = ptab.attrs 764 886 765 887 return ztab 766 888 767 889 def lbc_mask (ptab, nperio=None, cd_type='T', sval=np.nan) : 768 # 769 ''' 770 Mask fields on duplicated points 890 '''Mask fields on duplicated points 891 771 892 ptab : Input array. Rank 2 at least : ptab [...., lat, lon] 772 893 nperio : Type of periodicity 773 894 cd_type : Grid specification : T, U, V or F 774 895 775 896 See NEMO documentation for further details 776 897 ''' 777 jp j, jpi, nperio = lbc_init (ptab, nperio)778 ix, ax = __findAxis__ (ptab, 'x')779 jy, ay = __findAxis__ (ptab, 'y')898 jpi, nperio = lbc_init (ptab, nperio)[1:] 899 ax = __find_axis__ (ptab, 'x')[0] 900 ay = __find_axis__ (ptab, 'y')[0] 780 901 ztab = ptab.copy () 781 902 782 if ax : 903 if ax : 783 904 # 784 905 #> East-West boundary conditions … … 789 910 ztab [..., -1] = sval 790 911 791 if ay : 912 if ay : 792 913 # 793 914 #> South (in which nperio cases ?) … … 795 916 if nperio in [1, 3, 4, 5, 6] : 796 917 ztab [..., 0, :] = sval 797 918 798 919 # 799 920 #> North-South boundary conditions … … 803 924 ztab [..., -1, : ] = sval 804 925 ztab [..., -2, :jpi//2 ] = sval 805 926 806 927 if cd_type == 'U' : 807 ztab [..., -1, : ] = sval 928 ztab [..., -1, : ] = sval 808 929 ztab [..., -2, jpi//2+1: ] = sval 809 930 810 931 if cd_type == 'V' : 811 932 ztab [..., -2, : ] = sval 812 ztab [..., -1, : ] = sval 933 ztab [..., -1, : ] = sval 813 934 814 935 if cd_type == 'F' : … … 823 944 ztab [..., -1, jpi//2-1:-1] = sval 824 945 825 if cd_type == 'V' : 946 if cd_type == 'V' : 826 947 ztab [..., -1, 1: ] = sval 827 948 … … 834 955 835 956 if cd_type == 'U' : 836 ztab [..., -1, 0:-1 ] = sval 957 ztab [..., -1, 0:-1 ] = sval 837 958 ztab [..., -1, -1 ] = sval 838 959 … … 849 970 850 971 def lbc_plot (ptab, nperio=None, cd_type='T', psgn=1.0, sval=np.nan) : 851 ''' 852 Set periodicity on input field, adapted for plotting for any cartopy projection 972 '''Set periodicity on input field, for plotting for any cartopy projection 973 974 Points at the north fold are masked 975 Points for zonal periodicity are kept 853 976 ptab : Input array. Rank 2 at least : ptab[...., lat, lon] 854 977 nperio : Type of periodicity 855 978 cd_type : Grid specification : T, U, V or F 856 psgn : For change of sign for vector components (1 for scalars, -1 for vector components) 857 979 psgn : For change of sign for vector components 980 (1 for scalars, -1 for vector components) 981 858 982 See NEMO documentation for further details 859 983 ''' 860 jp j, jpi, nperio = lbc_init (ptab, nperio)861 ix, ax = __findAxis__ (ptab, 'x')862 jy, ay = __findAxis__ (ptab, 'y')984 jpi, nperio = lbc_init (ptab, nperio)[1:] 985 ax = __find_axis__ (ptab, 'x')[0] 986 ay = __find_axis__ (ptab, 'y')[0] 863 987 psgn = ptab.dtype.type (psgn) 864 988 ztab = ptab.copy () 865 989 866 if ax : 990 if ax : 867 991 # 868 992 #> East-West boundary conditions … … 873 997 ztab [..., :, -1] = ztab [..., :, 1] 874 998 875 if ay : 999 if ay : 876 1000 #> Masks south 877 1001 # ------------ 878 if nperio in [4, 6] : ztab [..., 0, : ] = sval 1002 if nperio in [4, 6] : 1003 ztab [..., 0, : ] = sval 879 1004 880 1005 # … … 889 1014 ztab [..., -1, : ] = sval 890 1015 891 if cd_type == 'V' : 1016 if cd_type == 'V' : 892 1017 ztab [..., -2, : ] = sval 893 1018 ztab [..., -1, : ] = sval … … 904 1029 ztab [..., -1, jpi//2-1:-1] = sval 905 1030 906 if cd_type == 'V' : 1031 if cd_type == 'V' : 907 1032 ztab [..., -1, 1: ] = sval 908 1033 … … 910 1035 ztab [..., -1, 0:-1 ] = sval 911 1036 912 if nperio in [5, 6] : # North fold F-point pivot 1037 if nperio in [5, 6] : # North fold F-point pivot 913 1038 if cd_type in ['T', 'W'] : 914 1039 ztab [..., -1, : ] = sval 915 1040 916 1041 if cd_type == 'U' : 917 ztab [..., -1, : ] = sval 1042 ztab [..., -1, : ] = sval 918 1043 919 1044 if cd_type == 'V' : … … 927 1052 return ztab 928 1053 929 def lbc_add (ptab, nperio=None, cd_type=None, psgn=1 , sval=None) :930 ''' 931 Handles NEMO domain changes between NEMO 4.0 to NEMO 4.2 932 Peridodicity halo has been removed1054 def lbc_add (ptab, nperio=None, cd_type=None, psgn=1) : 1055 '''Handles NEMO domain changes between NEMO 4.0 to NEMO 4.2 1056 1057 Periodicity and north fold halos has been removed in NEMO 4.2 933 1058 This routine adds the halos if needed 934 1059 935 ptab : Input array (works 1060 ptab : Input array (works 936 1061 rank 2 at least : ptab[...., lat, lon] 937 1062 nperio : Type of periodicity 938 1063 939 1064 See NEMO documentation for further details 940 1065 ''' 941 mmath = __mmath__ (ptab) 942 jpj, jpi, nperio = lbc_init (ptab, nperio)1066 mmath = __mmath__ (ptab) 1067 nperio = lbc_init (ptab, nperio)[-1] 943 1068 lshape = get_shape (ptab) 944 ix , ax = __findAxis__ (ptab, 'x')945 jy , ay = __findAxis__ (ptab, 'y')1069 ix = __find_axis__ (ptab, 'x')[-1] 1070 jy = __find_axis__ (ptab, 'y')[-1] 946 1071 947 1072 t_shape = np.array (ptab.shape) 948 1073 949 if nperio == 4.2 or nperio == 6.2:950 1074 if nperio in [4.2, 6.2] : 1075 951 1076 ext_shape = t_shape.copy() 952 if 'X' in lshape : ext_shape[ix] = ext_shape[ix] + 2 953 if 'Y' in lshape : ext_shape[jy] = ext_shape[jy] + 1 1077 if 'X' in lshape : 1078 ext_shape[ix] = ext_shape[ix] + 2 1079 if 'Y' in lshape : 1080 ext_shape[jy] = ext_shape[jy] + 1 954 1081 955 1082 if mmath == xr : … … 958 1085 ptab_ext.values[..., :-1, 1:-1] = ptab.values.copy () 959 1086 else : 960 if 'X' in lshape : ptab_ext.values[..., 1:-1] = ptab.values.copy () 961 if 'Y' in lshape : ptab_ext.values[..., :-1 ] = ptab.values.copy () 1087 if 'X' in lshape : 1088 ptab_ext.values[..., 1:-1] = ptab.values.copy () 1089 if 'Y' in lshape : 1090 ptab_ext.values[..., :-1 ] = ptab.values.copy () 962 1091 else : 963 1092 ptab_ext = np.zeros (ext_shape) 964 if 'X' in lshape and 'Y' in lshape : ptab_ext [..., :-1, 1:-1] = ptab.copy () 1093 if 'X' in lshape and 'Y' in lshape : 1094 ptab_ext [..., :-1, 1:-1] = ptab.copy () 965 1095 else : 966 if 'X' in lshape : ptab_ext [..., 1:-1] = ptab.copy () 967 if 'Y' in lshape : ptab_ext [..., :-1 ] = ptab.copy () 968 969 if nperio == 4.2 : ptab_ext = lbc (ptab_ext, nperio=4, cd_type=cd_type, psgn=psgn) 970 if nperio == 6.2 : ptab_ext = lbc (ptab_ext, nperio=6, cd_type=cd_type, psgn=psgn) 971 1096 if 'X' in lshape : 1097 ptab_ext [..., 1:-1] = ptab.copy () 1098 if 'Y' in lshape : 1099 ptab_ext [..., :-1 ] = ptab.copy () 1100 1101 if nperio == 4.2 : 1102 ptab_ext = lbc (ptab_ext, nperio=4, cd_type=cd_type, psgn=psgn) 1103 if nperio == 6.2 : 1104 ptab_ext = lbc (ptab_ext, nperio=6, cd_type=cd_type, psgn=psgn) 1105 972 1106 if mmath == xr : 973 1107 ptab_ext.attrs = ptab.attrs 974 kz, az = __findAxis__ (ptab, 'z') 975 it, at = __findAxis__ (ptab, 't') 976 if az : ptab_ext = ptab_ext.assign_coords ( {az:ptab.coords[az]} ) 977 if at : ptab_ext = ptab_ext.assign_coords ( {at:ptab.coords[at]} ) 1108 az = __find_axis__ (ptab, 'z')[0] 1109 at = __find_axis__ (ptab, 't')[0] 1110 if az : 1111 ptab_ext = ptab_ext.assign_coords ( {az:ptab.coords[az]} ) 1112 if at : 1113 ptab_ext = ptab_ext.assign_coords ( {at:ptab.coords[at]} ) 978 1114 979 1115 else : ptab_ext = lbc (ptab, nperio=nperio, cd_type=cd_type, psgn=psgn) 980 1116 981 1117 return ptab_ext 982 1118 983 1119 def lbc_del (ptab, nperio=None, cd_type='T', psgn=1) : 984 ''' 985 Handles NEMO domain changes between NEMO 4.0 to NEMO 4.2 986 Periodicity halo has been removed1120 '''Handles NEMO domain changes between NEMO 4.0 to NEMO 4.2 1121 1122 Periodicity and north fold halos has been removed in NEMO 4.2 987 1123 This routine removes the halos if needed 988 1124 989 ptab : Input array (works 1125 ptab : Input array (works 990 1126 rank 2 at least : ptab[...., lat, lon] 991 1127 nperio : Type of periodicity 992 1128 993 1129 See NEMO documentation for further details 994 1130 ''' 995 jpj, jpi, nperio = lbc_init (ptab, nperio) 996 lshape = get_shape (ptab) 997 ix, ax = __findAxis__ (ptab, 'x') 998 jy, ay = __findAxis__ (ptab, 'y') 999 1000 if nperio == 4.2 or nperio == 6.2 : 1001 if ax or ay : 1002 if ax and ay : 1003 return lbc (ptab[..., :-1, 1:-1], nperio=nperio, cd_type=cd_type, psgn=psgn) 1004 else : 1131 nperio = lbc_init (ptab, nperio)[-1] 1132 #lshape = get_shape (ptab) 1133 ax = __find_axis__ (ptab, 'x')[0] 1134 ay = __find_axis__ (ptab, 'y')[0] 1135 1136 if nperio in [4.2, 6.2] : 1137 if ax or ay : 1138 if ax and ay : 1139 ztab = lbc (ptab[..., :-1, 1:-1], 1140 nperio=nperio, cd_type=cd_type, psgn=psgn) 1141 else : 1005 1142 if ax : 1006 return lbc (ptab[..., 1:-1], nperio=nperio, cd_type=cd_type, psgn=psgn) 1143 ztab = lbc (ptab[..., 1:-1], 1144 nperio=nperio, cd_type=cd_type, psgn=psgn) 1007 1145 if ay : 1008 return lbc (ptab[..., -1], nperio=nperio, cd_type=cd_type, psgn=psgn) 1146 ztab = lbc (ptab[..., -1], 1147 nperio=nperio, cd_type=cd_type, psgn=psgn) 1009 1148 else : 1010 returnptab1149 ztab = ptab 1011 1150 else : 1012 return ptab 1151 ztab = ptab 1152 1153 return ztab 1013 1154 1014 1155 def lbc_index (jj, ii, jpj, jpi, nperio=None, cd_type='T') : 1015 ''' 1016 For indexes of a NEMO point, give the corresponding point inside the util domain 1156 '''For indexes of a NEMO point, give the corresponding point 1157 inside the domain (i.e. not in the halo) 1158 1017 1159 jj, ii : indexes 1018 1160 jpi, jpi : size of domain 1019 1161 nperio : type of periodicity 1020 1162 cd_type : grid specification : T, U, V or F 1021 1163 1022 1164 See NEMO documentation for further details 1023 1165 ''' 1024 1166 1025 if nperio == None : nperio = __guessNperio__ (jpj, jpi, nperio) 1026 1027 ## For the sake of simplicity, switch to the convention of original lbc Fortran routine from NEMO 1028 ## : starts indexes at 1 1029 jy = jj + 1 ; ix = ii + 1 1167 if nperio is None : 1168 nperio = __guess_nperio__ (jpj, jpi, nperio) 1169 1170 ## For the sake of simplicity, switch to the convention of original 1171 ## lbc Fortran routine from NEMO : starts indexes at 1 1172 jy = jj + 1 1173 ix = ii + 1 1030 1174 1031 1175 mmath = __mmath__ (jj) 1032 if mmath == None : mmath=np 1176 if mmath is None : 1177 mmath=np 1033 1178 1034 1179 # … … 1041 1186 1042 1187 # 1043 def mod IJ(cond, jy_new, ix_new) :1188 def mod_ij (cond, jy_new, ix_new) : 1044 1189 jy_r = mmath.where (cond, jy_new, jy) 1045 1190 ix_r = mmath.where (cond, ix_new, ix) … … 1050 1195 if nperio in [ 3 , 4 ] : 1051 1196 if cd_type in [ 'T' , 'W' ] : 1052 (jy, ix) = modIJ (np.logical_and (jy==jpj , ix>=2 ), jpj-2, jpi-ix+2) 1053 (jy, ix) = modIJ (np.logical_and (jy==jpj , ix==1 ), jpj-1, 3 ) 1054 (jy, ix) = modIJ (np.logical_and (jy==jpj-1, ix>=jpi//2+1), jy , jpi-ix+2) 1197 jy, ix = mod_ij (np.logical_and (jy==jpj , ix>=2 ), jpj-2, jpi-ix+2) 1198 jy, ix = mod_ij (np.logical_and (jy==jpj , ix==1 ), jpj-1, 3 ) 1199 jy, ix = mod_ij (np.logical_and (jy==jpj-1, ix>=jpi//2+1), 1200 jy , jpi-ix+2) 1055 1201 1056 1202 if cd_type in [ 'U' ] : 1057 (jy, ix) = modIJ (np.logical_and (jy==jpj , np.logical_and (ix>=1, ix <= jpi-1) ), jy , jpi-ix+1) 1058 (jy, ix) = modIJ (np.logical_and (jy==jpj , ix==1 ) , jpj-2, 2 ) 1059 (jy, ix) = modIJ (np.logical_and (jy==jpj , ix==jpi) , jpj-2, jpi-1 ) 1060 (jy, ix) = modIJ (np.logical_and (jy==jpj-1, np.logical_and (ix>=jpi//2, ix<=jpi-1)), jy , jpi-ix+1) 1061 1203 jy, ix = mod_ij (np.logical_and ( 1204 jy==jpj , 1205 np.logical_and (ix>=1, ix <= jpi-1) ), 1206 jy , jpi-ix+1) 1207 jy, ix = mod_ij (np.logical_and (jy==jpj , ix==1 ) , jpj-2, 2 ) 1208 jy, ix = mod_ij (np.logical_and (jy==jpj , ix==jpi) , jpj-2, jpi-1 ) 1209 jy, ix = mod_ij (np.logical_and (jy==jpj-1, 1210 np.logical_and (ix>=jpi//2, ix<=jpi-1)), jy , jpi-ix+1) 1211 1062 1212 if cd_type in [ 'V' ] : 1063 (jy, ix) = modIJ(np.logical_and (jy==jpj-1, ix>=2 ), jpj-2, jpi-ix+2)1064 (jy, ix) = modIJ(np.logical_and (jy==jpj , ix>=2 ), jpj-3, jpi-ix+2)1065 (jy, ix) = modIJ(np.logical_and (jy==jpj , ix==1 ), jpj-3, 3 )1066 1213 jy, ix = mod_ij (np.logical_and (jy==jpj-1, ix>=2 ), jpj-2, jpi-ix+2) 1214 jy, ix = mod_ij (np.logical_and (jy==jpj , ix>=2 ), jpj-3, jpi-ix+2) 1215 jy, ix = mod_ij (np.logical_and (jy==jpj , ix==1 ), jpj-3, 3 ) 1216 1067 1217 if cd_type in [ 'F' ] : 1068 (jy, ix) = modIJ(np.logical_and (jy==jpj-1, ix<=jpi-1), jpj-2, jpi-ix+1)1069 (jy, ix) = modIJ(np.logical_and (jy==jpj , ix<=jpi-1), jpj-3, jpi-ix+1)1070 (jy, ix) = modIJ(np.logical_and (jy==jpj , ix==1 ), jpj-3, 2 )1071 (jy, ix) = modIJ(np.logical_and (jy==jpj , ix==jpi ), jpj-3, jpi-1 )1218 jy, ix = mod_ij (np.logical_and (jy==jpj-1, ix<=jpi-1), jpj-2, jpi-ix+1) 1219 jy, ix = mod_ij (np.logical_and (jy==jpj , ix<=jpi-1), jpj-3, jpi-ix+1) 1220 jy, ix = mod_ij (np.logical_and (jy==jpj , ix==1 ), jpj-3, 2 ) 1221 jy, ix = mod_ij (np.logical_and (jy==jpj , ix==jpi ), jpj-3, jpi-1 ) 1072 1222 1073 1223 if nperio in [ 5 , 6 ] : 1074 1224 if cd_type in [ 'T' , 'W' ] : # T-, W-point 1075 (jy, ix) = modIJ(jy==jpj, jpj-1, jpi-ix+1)1076 1225 jy, ix = mod_ij (jy==jpj, jpj-1, jpi-ix+1) 1226 1077 1227 if cd_type in [ 'U' ] : # U-point 1078 (jy, ix) = modIJ(np.logical_and (jy==jpj , ix<=jpi-1 ), jpj-1, jpi-ix )1079 (jy, ix) = modIJ(np.logical_and (jy==jpj , ix==jpi ), jpi-1, 1 )1080 1228 jy, ix = mod_ij (np.logical_and (jy==jpj , ix<=jpi-1 ), jpj-1, jpi-ix ) 1229 jy, ix = mod_ij (np.logical_and (jy==jpj , ix==jpi ), jpi-1, 1 ) 1230 1081 1231 if cd_type in [ 'V' ] : # V-point 1082 (jy, ix) = modIJ(jy==jpj , jy , jpi-ix+1)1083 (jy, ix) = modIJ(np.logical_and (jy==jpj-1, ix>=jpi//2+1), jy , jpi-ix+1)1084 1232 jy, ix = mod_ij (jy==jpj , jy , jpi-ix+1) 1233 jy, ix = mod_ij (np.logical_and (jy==jpj-1, ix>=jpi//2+1), jy , jpi-ix+1) 1234 1085 1235 if cd_type in [ 'F' ] : # F-point 1086 (jy, ix) = modIJ(np.logical_and (jy==jpj , ix<=jpi-1 ), jpj-2, jpi-ix )1087 (jy, ix) = modIJ(np.logical_and (ix==jpj , ix==jpi ), jpj-2, 1 )1088 (jy, ix) = modIJ(np.logical_and (jy==jpj-1, ix>=jpi//2+1), jy , jpi-ix )1236 jy, ix = mod_ij (np.logical_and (jy==jpj , ix<=jpi-1 ), jpj-2, jpi-ix ) 1237 jy, ix = mod_ij (np.logical_and (ix==jpj , ix==jpi ), jpj-2, 1 ) 1238 jy, ix = mod_ij (np.logical_and (jy==jpj-1, ix>=jpi//2+1), jy , jpi-ix ) 1089 1239 1090 1240 ## Restore convention to Python/C : indexes start at 0 1091 jy += -1 ; ix += -1 1092 1093 if isinstance (jj, int) : jy = jy.item () 1094 if isinstance (ii, int) : ix = ix.item () 1241 jy += -1 1242 ix += -1 1243 1244 if isinstance (jj, int) : 1245 jy = jy.item () 1246 if isinstance (ii, int) : 1247 ix = ix.item () 1095 1248 1096 1249 return jy, ix 1097 1098 def findJI (lat_data, lon_data, lat_grid, lon_grid, mask=1.0, verbose=False, out=None) : 1099 ''' 1100 Description: seeks J,I indices of the grid point which is the closest of a given point 1250 1251 def find_ji (lat_data, lon_data, lat_grid, lon_grid, mask=1.0, verbose=False, out=None) : 1252 ''' 1253 Description: seeks J,I indices of the grid point which is the closest 1254 of a given point 1255 1101 1256 Usage: go FindJI <data latitude> <data longitude> <grid latitudes> <grid longitudes> [mask] 1102 < longitude fields> <latitude field> are 2D fields on J/I (Y/X) dimensions1257 <grid latitudes><grid longitudes> are 2D fields on J/I (Y/X) dimensions 1103 1258 mask : if given, seek only non masked grid points (i.e with mask=1) 1104 1259 1105 1260 Example : findIJ (40, -20, nav_lat, nav_lon, mask=1.0) 1106 1261 1107 1262 Note : all longitudes and latitudes in degrees 1108 1263 1109 1264 Note : may work with 1D lon/lat (?) 1110 1265 ''' 1111 1266 # Get grid dimensions 1112 if len (lon_grid.shape) == 2 : (jpj, jpi) = lon_grid.shape 1113 else : jpj = len(lat_grid) ; jpi=len(lon_grid) 1114 1115 mmath = __mmath__ (lat_grid) 1116 1117 # Compute distance from the point to all grid points (in radian) 1118 arg = np.sin (rad*lat_data) * np.sin (rad*lat_grid) \ 1119 + np.cos (rad*lat_data) * np.cos (rad*lat_grid) * np.cos(rad*(lon_data-lon_grid)) 1120 distance = np.arccos (arg) + 4.0*rpi*(1.0-mask) # Send masked points to 'infinite' 1267 if len (lon_grid.shape) == 2 : 1268 jpi = lon_grid.shape[-1] 1269 else : 1270 jpi = len(lon_grid) 1271 1272 #mmath = __mmath__ (lat_grid) 1273 1274 # Compute distance from the point to all grid points (in RADian) 1275 arg = ( np.sin (RAD*lat_data) * np.sin (RAD*lat_grid) 1276 + np.cos (RAD*lat_data) * np.cos (RAD*lat_grid) * 1277 np.cos(RAD*(lon_data-lon_grid)) ) 1278 # Send masked points to 'infinite' 1279 distance = np.arccos (arg) + 4.0*RPI*(1.0-mask) 1121 1280 1122 1281 # Truncates to alleviate some precision problem with some grids … … 1126 1285 # Compute minimum of distance, and index of minimum 1127 1286 # 1128 distance_min = distance.min ()1287 #distance_min = distance.min () 1129 1288 jimin = int (distance.argmin ()) 1130 1131 # Compute 2D indices 1132 jmin = jimin // jpi ; imin = jimin - jmin*jpi 1133 1289 1290 # Compute 2D indices (Python/C flavor : starting at 0) 1291 jmin = jimin // jpi 1292 imin = jimin - jmin*jpi 1293 1134 1294 # Result 1135 1295 if verbose : 1136 1296 # Compute distance achieved 1137 mindist = distance [jmin, imin]1138 1297 #mindist = distance [jmin, imin] 1298 1139 1299 # Compute azimuth 1140 1300 dlon = lon_data-lon_grid[jmin,imin] 1141 arg = np.sin (rad*dlon) / (np.cos(rad*lat_data)*np.tan(rad*lat_grid[jmin,imin]) - np.sin(rad*lat_data)*np.cos(rad*dlon)) 1142 azimuth = dar*np.arctan (arg) 1143 print ( f'I={imin:d} J={jmin:d} - Data:{lat_data:5.1f}°N {lon_data:5.1f}°E - Grid:{lat_grid[jmin,imin]:4.1f}°N ' \ 1144 + f'{lon_grid[jmin,imin]:4.1f}°E - Dist: {ra*distance[jmin,imin]:6.1f}km {dar*distance[jmin,imin]:5.2f}° ' \ 1145 + f'- Azimuth: {rad*azimuth:3.2f}rad - {azimuth:5.1f}°' ) 1146 1147 if out=='dict' : return {'x':imin, 'y':jmin} 1148 elif out=='array' or out=='numpy' or out=='np': return np.array ( [jmin, imin] ) 1149 elif out=='xarray' or out=='xr' : return xr.DataArray ( [jmin, imin] ) 1150 elif out=='list' : return [jmin, imin] 1151 elif out=='tuple' : return jmin, imin 1152 else : return jmin, imin 1153 1154 def geo2en (pxx, pyy, pzz, glam, gphi) : 1155 ''' 1156 Change vector from geocentric to east/north 1301 arg = np.sin (RAD*dlon) / ( 1302 np.cos(RAD*lat_data)*np.tan(RAD*lat_grid[jmin,imin]) 1303 - np.sin(RAD*lat_data)*np.cos(RAD*dlon)) 1304 azimuth = DAR*np.arctan (arg) 1305 print ( f'I={imin:d} J={jmin:d} - Data:{lat_data:5.1f}°N {lon_data:5.1f}°E' \ 1306 + f'- Grid:{lat_grid[jmin,imin]:4.1f}°N ' \ 1307 + f'{lon_grid[jmin,imin]:4.1f}°E - Dist: {RA*distance[jmin,imin]:6.1f}km' \ 1308 + f' {DAR*distance[jmin,imin]:5.2f}° ' \ 1309 + f'- Azimuth: {RAD*azimuth:3.2f}RAD - {azimuth:5.1f}°' ) 1310 1311 if out=='dict' : 1312 return {'x':imin, 'y':jmin} 1313 elif out in ['array', 'numpy', 'np'] : 1314 return np.array ( [jmin, imin] ) 1315 elif out in ['xarray', 'xr'] : 1316 return xr.DataArray ( [jmin, imin] ) 1317 elif out=='list' : 1318 return [jmin, imin] 1319 elif out=='tuple' : 1320 return jmin, imin 1321 else : 1322 return jmin, imin 1323 1324 def curl (tx, ty, e1f, e2f, nperio=None) : 1325 '''Returns curl of a vector field 1326 ''' 1327 ax = __find_axis__ (tx, 'x')[0] 1328 ay = __find_axis__ (ty, 'y')[0] 1329 1330 tx_0 = lbc_add (tx , nperio=nperio, cd_type='U', psgn=-1) 1331 ty_0 = lbc_add (ty , nperio=nperio, cd_type='V', psgn=-1) 1332 e1f_0 = lbc_add (e1f, nperio=nperio, cd_type='U', psgn=-1) 1333 e2f_0 = lbc_add (e2f, nperio=nperio, cd_type='V', psgn=-1) 1334 1335 tx_1 = tx_0.roll ( {ay:-1} ) 1336 ty_1 = ty_0.roll ( {ax:-1} ) 1337 tx_1 = lbc (tx_1, nperio=nperio, cd_type='U', psgn=-1) 1338 ty_1 = lbc (ty_1, nperio=nperio, cd_type='V', psgn=-1) 1339 1340 zcurl = (ty_1 - ty_0)/e1f_0 - (tx_1 - tx_0)/e2f_0 1341 1342 mask = np.logical_or ( 1343 np.logical_or ( np.isnan(tx_0), np.isnan(tx_1)), 1344 np.logical_or ( np.isnan(ty_0), np.isnan(ty_1)) ) 1345 1346 zcurl = zcurl.where (np.logical_not (mask), np.nan) 1347 1348 zcurl = lbc_del (zcurl, nperio=nperio, cd_type='F', psgn=1) 1349 zcurl = lbc (zcurl, nperio=nperio, cd_type='F', psgn=1) 1350 1351 return zcurl 1352 1353 def div (ux, uy, e1t, e2t, nperio=None) : 1354 '''Returns divergence of a vector field 1355 ''' 1356 ax = __find_axis__ (ux, 'x')[0] 1357 ay = __find_axis__ (ux, 'y')[0] 1358 1359 ux_0 = lbc_add (ux , nperio=nperio, cd_type='U', psgn=-1) 1360 uy_0 = lbc_add (uy , nperio=nperio, cd_type='V', psgn=-1) 1361 e1t_0 = lbc_add (e1t, nperio=nperio, cd_type='U', psgn=-1) 1362 e2t_0 = lbc_add (e2t, nperio=nperio, cd_type='V', psgn=-1) 1363 1364 ux_1 = ux_0.roll ( {ay:1} ) 1365 uy_1 = uy_0.roll ( {ax:1} ) 1366 ux_1 = lbc (ux_1, nperio=nperio, cd_type='U', psgn=-1) 1367 uy_1 = lbc (uy_1, nperio=nperio, cd_type='V', psgn=-1) 1368 1369 zdiv = (ux_0 - ux_1)/e2t_0 + (uy_0 - uy_1)/e1t_0 1370 1371 mask = np.logical_or ( 1372 np.logical_or ( np.isnan(ux_0), np.isnan(ux_1)), 1373 np.logical_or ( np.isnan(uy_0), np.isnan(uy_1)) ) 1374 1375 zdiv = zdiv.where (np.logical_not (mask), np.nan) 1376 1377 zdiv = lbc_del (zdiv, nperio=nperio, cd_type='T', psgn=1) 1378 zdiv = lbc (zdiv, nperio=nperio, cd_type='T', psgn=1) 1379 1380 return zdiv 1381 1382 def geo2en (pxx, pyy, pzz, glam, gphi) : 1383 '''Change vector from geocentric to east/north 1157 1384 1158 1385 Inputs : … … 1161 1388 ''' 1162 1389 1163 gsinlon = np.sin ( rad* glam)1164 gcoslon = np.cos ( rad* glam)1165 gsinlat = np.sin ( rad* gphi)1166 gcoslat = np.cos ( rad* gphi)1167 1390 gsinlon = np.sin (RAD * glam) 1391 gcoslon = np.cos (RAD * glam) 1392 gsinlat = np.sin (RAD * gphi) 1393 gcoslat = np.cos (RAD * gphi) 1394 1168 1395 pte = - pxx * gsinlon + pyy * gcoslon 1169 1396 ptn = - pxx * gcoslon * gsinlat - pyy * gsinlon * gsinlat + pzz * gcoslat … … 1172 1399 1173 1400 def en2geo (pte, ptn, glam, gphi) : 1174 ''' 1175 Change vector from east/north to geocentric 1176 1177 Inputs : 1401 '''Change vector from east/north to geocentric 1402 1403 Inputs : 1178 1404 pte, ptn : eastward/northward components 1179 1405 glam, gphi : longitude and latitude of the points 1180 1406 ''' 1181 1182 gsinlon = np.sin ( rad* glam)1183 gcoslon = np.cos ( rad* glam)1184 gsinlat = np.sin ( rad* gphi)1185 gcoslat = np.cos ( rad* gphi)1407 1408 gsinlon = np.sin (RAD * glam) 1409 gcoslon = np.cos (RAD * glam) 1410 gsinlat = np.sin (RAD * gphi) 1411 gcoslat = np.cos (RAD * gphi) 1186 1412 1187 1413 pxx = - pte * gsinlon - ptn * gcoslon * gsinlat 1188 1414 pyy = pte * gcoslon - ptn * gsinlon * gsinlat 1189 1415 pzz = ptn * gcoslat 1190 1416 1191 1417 return pxx, pyy, pzz 1192 1418 1193 1419 1194 1420 def clo_lon (lon, lon0=0., rad=False, deg=True) : 1195 '''Choose closest to lon0 longitude, adding or substacting 360° if needed''' 1421 '''Choose closest to lon0 longitude, adding/substacting 360° 1422 if needed 1423 ''' 1196 1424 mmath = __mmath__ (lon, np) 1197 if rad : lon_range = 2.*np.pi 1198 if deg : lon_range = 360. 1199 clo_lon = lon 1200 clo_lon = mmath.where (clo_lon > lon0 + lon_range*0.5, clo_lon-lon_range, clo_lon) 1201 clo_lon = mmath.where (clo_lon < lon0 - lon_range*0.5, clo_lon+lon_range, clo_lon) 1202 clo_lon = mmath.where (clo_lon > lon0 + lon_range*0.5, clo_lon-lon_range, clo_lon) 1203 clo_lon = mmath.where (clo_lon < lon0 - lon_range*0.5, clo_lon+lon_range, clo_lon) 1204 if clo_lon.shape == () : clo_lon = clo_lon.item () 1425 if rad : 1426 lon_range = 2.*np.pi 1427 if deg : 1428 lon_range = 360. 1429 c_lon = lon 1430 c_lon = mmath.where (c_lon > lon0 + lon_range*0.5, 1431 c_lon-lon_range, clo_lon) 1432 c_lon = mmath.where (c_lon < lon0 - lon_range*0.5, 1433 c_lon+lon_range, clo_lon) 1434 c_lon = mmath.where (c_lon > lon0 + lon_range*0.5, 1435 c_lon-lon_range, clo_lon) 1436 c_lon = mmath.where (c_lon < lon0 - lon_range*0.5, 1437 c_lon+lon_range, clo_lon) 1438 if c_lon.shape == () : 1439 c_lon = c_lon.item () 1205 1440 if mmath == xr : 1206 try : 1207 for attr in lon.attrs : clo_lon.attrs[attr] = lon.attrs[attr] 1208 except : 1209 pass 1210 return clo_lon 1211 1441 if lon.attrs : 1442 c_lon.attrs.update ( lon.attrs ) 1443 return c_lon 1212 1444 1213 1445 def index2depth (pk, gdept_0) : 1214 ''' 1215 From index (real, continuous), get depth 1446 '''From index (real, continuous), get depth 1447 1448 Needed to use transforms in Matplotlib 1216 1449 ''' 1217 1450 jpk = gdept_0.shape[0] … … 1225 1458 1226 1459 def depth2index (pz, gdept_0) : 1227 ''' 1228 From depth, get index (real, continuous) 1460 '''From depth, get index (real, continuous) 1461 1462 Needed to use transforms in Matplotlib 1229 1463 ''' 1230 1464 jpk = gdept_0.shape[0] 1231 if type (pz) == xr.core.dataarray.DataArray:1465 if isinstance (pz, xr.core.dataarray.DataArray ) : 1232 1466 zz = xr.DataArray (pz.values, dims=('zz',)) 1233 elif type (pz) == np.ndarray:1467 elif isinstance (pz, np.ndarray) : 1234 1468 zz = xr.DataArray (pz.ravel(), dims=('zz',)) 1235 1469 else : 1236 1470 zz = xr.DataArray (np.array([pz]).ravel(), dims=('zz',)) 1237 1471 zz = np.minimum (gdept_0[-1], np.maximum (0, zz)) 1238 1472 1239 1473 idk1 = np.minimum ( (gdept_0-zz), 0.).argmax (axis=0).astype(int) 1240 1474 idk1 = np.maximum (0, np.minimum (jpk-1, idk1 )) 1241 1475 idk2 = np.maximum (0, np.minimum (jpk-1, idk1-1)) 1242 1243 ff = (zz - gdept_0[idk2])/(gdept_0[idk1]-gdept_0[idk2])1244 idk = ff*idk1 + (1.0-ff)*idk21476 1477 zff = (zz - gdept_0[idk2])/(gdept_0[idk1]-gdept_0[idk2]) 1478 idk = zff*idk1 + (1.0-zff)*idk2 1245 1479 idk = xr.where ( np.isnan(idk), idk1, idk) 1246 1480 return idk.values 1247 1481 1248 1482 def index2depth_panels (pk, gdept_0, depth0, fact) : 1249 ''' 1250 From index (real, continuous), get depth, with bottom part compressed 1483 '''From index (real, continuous), get depth, with bottom part compressed 1484 1485 Needed to use transforms in Matplotlib 1251 1486 ''' 1252 1487 jpk = gdept_0.shape[0] … … 1261 1496 1262 1497 def depth2index_panels (pz, gdept_0, depth0, fact) : 1263 ''' 1264 From index (real, continuous), get depth, with bottom part compressed 1498 '''From index (real, continuous), get depth, with bottom part compressed 1499 1500 Needed to use transforms in Matplotlib 1265 1501 ''' 1266 1502 jpk = gdept_0.shape[0] 1267 if type (pz) == xr.core.dataarray.DataArray:1503 if isinstance (pz, xr.core.dataarray.DataArray) : 1268 1504 zz = xr.DataArray (pz.values , dims=('zz',)) 1269 elif type (pz) == np.ndarray:1505 elif isinstance (pz, np.ndarray) : 1270 1506 zz = xr.DataArray (pz.ravel(), dims=('zz',)) 1271 else : 1507 else : 1272 1508 zz = xr.DataArray (np.array([pz]).ravel(), dims=('zz',)) 1273 1509 zz = np.minimum (gdept_0[-1], np.maximum (0, zz)) 1274 gdept_comp = xr.where ( gdept_0>depth0, (gdept_0-depth0)*fact+depth0, gdept_0) 1275 zz_comp = xr.where ( zz >depth0, (zz -depth0)*fact+depth0, zz ) 1510 gdept_comp = xr.where ( gdept_0>depth0, 1511 (gdept_0-depth0)*fact+depth0, gdept_0) 1512 zz_comp = xr.where ( zz >depth0, (zz -depth0)*fact+depth0, 1513 zz ) 1276 1514 zz_comp = np.minimum (gdept_comp[-1], np.maximum (0, zz_comp)) 1277 1515 … … 1279 1517 idk1 = np.maximum (0, np.minimum (jpk-1, idk1 )) 1280 1518 idk2 = np.maximum (0, np.minimum (jpk-1, idk1-1)) 1281 1282 ff = (zz_comp - gdept_0[idk2])/(gdept_0[idk1]-gdept_0[idk2])1283 idk = ff*idk1 + (1.0-ff)*idk21519 1520 zff = (zz_comp - gdept_0[idk2])/(gdept_0[idk1]-gdept_0[idk2]) 1521 idk = zff*idk1 + (1.0-zff)*idk2 1284 1522 idk = xr.where ( np.isnan(idk), idk1, idk) 1285 1523 return idk.values 1286 1524 1287 1525 def depth2comp (pz, depth0, fact ) : 1288 ''' 1289 Form depth, get compressed depth, with bottom part compressed 1526 '''Form depth, get compressed depth, with bottom part compressed 1527 1528 Needed to use transforms in Matplotlib 1290 1529 ''' 1291 1530 #print ('start depth2comp') 1292 if type (pz) == xr.core.dataarray.DataArray:1531 if isinstance (pz, xr.core.dataarray.DataArray) : 1293 1532 zz = pz.values 1294 elif type(pz) == list:1533 elif isinstance(pz, list) : 1295 1534 zz = np.array (pz) 1296 else : 1535 else : 1297 1536 zz = pz 1298 1537 gz = np.where ( zz>depth0, (zz-depth0)*fact+depth0, zz) 1299 1538 #print ( f'depth2comp : {gz=}' ) 1300 if type (pz) in [int, float] : return gz.item() 1301 else : return gz 1302 #print ('end depth2comp') 1539 if type (pz) in [int, float] : 1540 return gz.item() 1541 else : 1542 return gz 1303 1543 1304 1544 def comp2depth (pz, depth0, fact ) : 1305 ''' 1306 Form compressed depth, get depth, with bottom part compressed 1307 ''' 1308 if type (pz) == xr.core.dataarray.DataArray : 1545 '''Form compressed depth, get depth, with bottom part compressed 1546 1547 Needed to use transforms in Matplotlib 1548 ''' 1549 if isinstance (pz, xr.core.dataarray.DataArray) : 1309 1550 zz = pz.values 1310 elif type(pz) == list:1551 elif isinstance (pz, list) : 1311 1552 zz = np.array (pz) 1312 else : 1553 else : 1313 1554 zz = pz 1314 1555 gz = np.where ( zz>depth0, (zz-depth0)/fact+depth0, zz) 1315 if type (pz) in [int, float] : return gz.item() 1316 else : return gz 1317 1318 def index2lon (pi, lon1D) : 1319 ''' 1320 From index (real, continuous), get longitude 1321 ''' 1322 jpi = lon1D.shape[0] 1556 if type (pz) in [int, float] : 1557 gz = gz.item() 1558 1559 return gz 1560 1561 def index2lon (pi, plon_1d) : 1562 '''From index (real, continuous), get longitude 1563 1564 Needed to use transforms in Matplotlib 1565 ''' 1566 jpi = plon_1d.shape[0] 1323 1567 ii = xr.DataArray (pi) 1324 1568 i = np.maximum (0, np.minimum (jpi-1, ii )) … … 1326 1570 i1 = np.maximum (0, np.minimum (jpi-1, i0+1)) 1327 1571 xx = i - i0 1328 gx = (1.0-xx)* lon1D[i0]+ xx*lon1D[i1]1572 gx = (1.0-xx)*plon_1d[i0]+ xx*plon_1d[i1] 1329 1573 return gx.values 1330 1574 1331 def lon2index (px, lon1D) : 1332 ''' 1333 From longitude, get index (real, continuous) 1334 ''' 1335 jpi = lon1D.shape[0] 1336 if type (px) == xr.core.dataarray.DataArray : 1575 def lon2index (px, plon_1d) : 1576 '''From longitude, get index (real, continuous) 1577 1578 Needed to use transforms in Matplotlib 1579 ''' 1580 jpi = plon_1d.shape[0] 1581 if isinstance (px, xr.core.dataarray.DataArray) : 1337 1582 xx = xr.DataArray (px.values , dims=('xx',)) 1338 elif type (px) == np.ndarray:1583 elif isinstance (px, np.ndarray) : 1339 1584 xx = xr.DataArray (px.ravel(), dims=('xx',)) 1340 else : 1585 else : 1341 1586 xx = xr.DataArray (np.array([px]).ravel(), dims=('xx',)) 1342 xx = xr.where ( xx> lon1D.max(), xx-360.0, xx)1343 xx = xr.where ( xx< lon1D.min(), xx+360.0, xx)1344 xx = np.minimum ( lon1D.max(), np.maximum(xx, lon1D.min() ))1345 idi1 = np.minimum ( ( lon1D-xx), 0.).argmax (axis=0).astype(int)1587 xx = xr.where ( xx>plon_1d.max(), xx-360.0, xx) 1588 xx = xr.where ( xx<plon_1d.min(), xx+360.0, xx) 1589 xx = np.minimum (plon_1d.max(), np.maximum(xx, plon_1d.min() )) 1590 idi1 = np.minimum ( (plon_1d-xx), 0.).argmax (axis=0).astype(int) 1346 1591 idi1 = np.maximum (0, np.minimum (jpi-1, idi1 )) 1347 1592 idi2 = np.maximum (0, np.minimum (jpi-1, idi1-1)) 1348 1349 ff = (xx - lon1D[idi2])/(lon1D[idi1]-lon1D[idi2])1350 idi = ff*idi1 + (1.0-ff)*idi21593 1594 zff = (xx - plon_1d[idi2])/(plon_1d[idi1]-plon_1d[idi2]) 1595 idi = zff*idi1 + (1.0-zff)*idi2 1351 1596 idi = xr.where ( np.isnan(idi), idi1, idi) 1352 1597 return idi.values 1353 1598 1354 def index2lat (pj, lat1D) : 1355 ''' 1356 From index (real, continuous), get latitude 1357 ''' 1358 jpj = lat1D.shape[0] 1599 def index2lat (pj, plat_1d) : 1600 '''From index (real, continuous), get latitude 1601 1602 Needed to use transforms in Matplotlib 1603 ''' 1604 jpj = plat_1d.shape[0] 1359 1605 jj = xr.DataArray (pj) 1360 1606 j = np.maximum (0, np.minimum (jpj-1, jj )) … … 1362 1608 j1 = np.maximum (0, np.minimum (jpj-1, j0+1)) 1363 1609 yy = j - j0 1364 gy = (1.0-yy)* lat1D[j0]+ yy*lat1D[j1]1610 gy = (1.0-yy)*plat_1d[j0]+ yy*plat_1d[j1] 1365 1611 return gy.values 1366 1612 1367 def lat2index (py, lat1D) : 1368 ''' 1369 From latitude, get index (real, continuous) 1370 ''' 1371 jpj = lat1D.shape[0] 1372 if type (py) == xr.core.dataarray.DataArray : 1613 def lat2index (py, plat_1d) : 1614 '''From latitude, get index (real, continuous) 1615 1616 Needed to use transforms in Matplotlib 1617 ''' 1618 jpj = plat_1d.shape[0] 1619 if isinstance (py, xr.core.dataarray.DataArray) : 1373 1620 yy = xr.DataArray (py.values , dims=('yy',)) 1374 elif type (py) == np.ndarray:1621 elif isinstance (py, np.ndarray) : 1375 1622 yy = xr.DataArray (py.ravel(), dims=('yy',)) 1376 else : 1623 else : 1377 1624 yy = xr.DataArray (np.array([py]).ravel(), dims=('yy',)) 1378 yy = np.minimum ( lat1D.max(), np.minimum(yy, lat1D.max() ))1379 idj1 = np.minimum ( ( lat1D-yy), 0.).argmax (axis=0).astype(int)1625 yy = np.minimum (plat_1d.max(), np.minimum(yy, plat_1d.max() )) 1626 idj1 = np.minimum ( (plat_1d-yy), 0.).argmax (axis=0).astype(int) 1380 1627 idj1 = np.maximum (0, np.minimum (jpj-1, idj1 )) 1381 1628 idj2 = np.maximum (0, np.minimum (jpj-1, idj1-1)) 1382 1383 ff = (yy - lat1D[idj2])/(lat1D[idj1]-lat1D[idj2])1384 idj = ff*idj1 + (1.0-ff)*idj21629 1630 zff = (yy - plat_1d[idj2])/(plat_1d[idj1]-plat_1d[idj2]) 1631 idj = zff*idj1 + (1.0-zff)*idj2 1385 1632 idj = xr.where ( np.isnan(idj), idj1, idj) 1386 1633 return idj.values 1387 1634 1388 def angle_full (glamt, gphit, glamu, gphiu, glamv, gphiv, glamf, gphif, nperio=None) : 1389 '''Compute sinus and cosinus of model line direction with respect to east''' 1635 def angle_full (glamt, gphit, glamu, gphiu, glamv, gphiv, 1636 glamf, gphif, nperio=None) : 1637 '''Computes sinus and cosinus of model line direction with 1638 respect to east 1639 ''' 1390 1640 mmath = __mmath__ (glamt) 1391 1641 … … 1398 1648 zlamf = lbc_add (glamf, nperio, 'F', 1.) 1399 1649 zphif = lbc_add (gphif, nperio, 'F', 1.) 1400 1650 1401 1651 # north pole direction & modulous (at T-point) 1402 zxnpt = 0. - 2.0 * np.cos ( rad*zlamt) * np.tan (rpi/4.0 - rad*zphit/2.0)1403 zynpt = 0. - 2.0 * np.sin ( rad*zlamt) * np.tan (rpi/4.0 - rad*zphit/2.0)1652 zxnpt = 0. - 2.0 * np.cos (RAD*zlamt) * np.tan (RPI/4.0 - RAD*zphit/2.0) 1653 zynpt = 0. - 2.0 * np.sin (RAD*zlamt) * np.tan (RPI/4.0 - RAD*zphit/2.0) 1404 1654 znnpt = zxnpt*zxnpt + zynpt*zynpt 1405 1655 1406 1656 # north pole direction & modulous (at U-point) 1407 zxnpu = 0. - 2.0 * np.cos ( rad*zlamu) * np.tan (rpi/4.0 - rad*zphiu/2.0)1408 zynpu = 0. - 2.0 * np.sin ( rad*zlamu) * np.tan (rpi/4.0 - rad*zphiu/2.0)1657 zxnpu = 0. - 2.0 * np.cos (RAD*zlamu) * np.tan (RPI/4.0 - RAD*zphiu/2.0) 1658 zynpu = 0. - 2.0 * np.sin (RAD*zlamu) * np.tan (RPI/4.0 - RAD*zphiu/2.0) 1409 1659 znnpu = zxnpu*zxnpu + zynpu*zynpu 1410 1660 1411 1661 # north pole direction & modulous (at V-point) 1412 zxnpv = 0. - 2.0 * np.cos ( rad*zlamv) * np.tan (rpi/4.0 - rad*zphiv/2.0)1413 zynpv = 0. - 2.0 * np.sin ( rad*zlamv) * np.tan (rpi/4.0 - rad*zphiv/2.0)1662 zxnpv = 0. - 2.0 * np.cos (RAD*zlamv) * np.tan (RPI/4.0 - RAD*zphiv/2.0) 1663 zynpv = 0. - 2.0 * np.sin (RAD*zlamv) * np.tan (RPI/4.0 - RAD*zphiv/2.0) 1414 1664 znnpv = zxnpv*zxnpv + zynpv*zynpv 1415 1665 1416 1666 # north pole direction & modulous (at F-point) 1417 zxnpf = 0. - 2.0 * np.cos( rad*zlamf ) * np.tan ( rpi/4. - rad*zphif/2. )1418 zynpf = 0. - 2.0 * np.sin( rad*zlamf ) * np.tan ( rpi/4. - rad*zphif/2. )1667 zxnpf = 0. - 2.0 * np.cos( RAD*zlamf ) * np.tan ( RPI/4. - RAD*zphif/2. ) 1668 zynpf = 0. - 2.0 * np.sin( RAD*zlamf ) * np.tan ( RPI/4. - RAD*zphif/2. ) 1419 1669 znnpf = zxnpf*zxnpf + zynpf*zynpf 1420 1670 1421 1671 # j-direction: v-point segment direction (around T-point) 1422 zlam = zlamv 1672 zlam = zlamv 1423 1673 zphi = zphiv 1424 1674 zlan = np.roll ( zlamv, axis=-2, shift=1) # glamv (ji,jj-1) 1425 1675 zphh = np.roll ( zphiv, axis=-2, shift=1) # gphiv (ji,jj-1) 1426 zxvvt = 2.0 * np.cos ( rad*zlam ) * np.tan ( rpi/4. - rad*zphi/2. )\1427 - 2.0 * np.cos ( rad*zlan ) * np.tan ( rpi/4. - rad*zphh/2. )1428 zyvvt = 2.0 * np.sin ( rad*zlam ) * np.tan ( rpi/4. - rad*zphi/2. )\1429 - 2.0 * np.sin ( rad*zlan ) * np.tan ( rpi/4. - rad*zphh/2. )1676 zxvvt = 2.0 * np.cos ( RAD*zlam ) * np.tan ( RPI/4. - RAD*zphi/2. ) \ 1677 - 2.0 * np.cos ( RAD*zlan ) * np.tan ( RPI/4. - RAD*zphh/2. ) 1678 zyvvt = 2.0 * np.sin ( RAD*zlam ) * np.tan ( RPI/4. - RAD*zphi/2. ) \ 1679 - 2.0 * np.sin ( RAD*zlan ) * np.tan ( RPI/4. - RAD*zphh/2. ) 1430 1680 znvvt = np.sqrt ( znnpt * ( zxvvt*zxvvt + zyvvt*zyvvt ) ) 1431 1681 … … 1435 1685 zlan = np.roll (zlamf, axis=-2, shift=1) # glamf (ji,jj-1) 1436 1686 zphh = np.roll (zphif, axis=-2, shift=1) # gphif (ji,jj-1) 1437 zxffu = 2.0 * np.cos ( rad*zlam ) * np.tan ( rpi/4. - rad*zphi/2. )\1438 - 2.0 * np.cos ( rad*zlan ) * np.tan ( rpi/4. - rad*zphh/2. )1439 zyffu = 2.0 * np.sin ( rad*zlam ) * np.tan ( rpi/4. - rad*zphi/2. )\1440 - 2.0 * np.sin ( rad*zlan ) * np.tan ( rpi/4. - rad*zphh/2. )1687 zxffu = 2.0 * np.cos ( RAD*zlam ) * np.tan ( RPI/4. - RAD*zphi/2. ) \ 1688 - 2.0 * np.cos ( RAD*zlan ) * np.tan ( RPI/4. - RAD*zphh/2. ) 1689 zyffu = 2.0 * np.sin ( RAD*zlam ) * np.tan ( RPI/4. - RAD*zphi/2. ) \ 1690 - 2.0 * np.sin ( RAD*zlan ) * np.tan ( RPI/4. - RAD*zphh/2. ) 1441 1691 znffu = np.sqrt ( znnpu * ( zxffu*zxffu + zyffu*zyffu ) ) 1442 1692 1443 1693 # i-direction: f-point segment direction (around v-point) 1444 zlam = zlamf 1694 zlam = zlamf 1445 1695 zphi = zphif 1446 1696 zlan = np.roll (zlamf, axis=-1, shift=1) # glamf (ji-1,jj) 1447 1697 zphh = np.roll (zphif, axis=-1, shift=1) # gphif (ji-1,jj) 1448 zxffv = 2.0 * np.cos ( rad*zlam ) * np.tan ( rpi/4. - rad*zphi/2. )\1449 - 2.0 * np.cos ( rad*zlan ) * np.tan ( rpi/4. - rad*zphh/2. )1450 zyffv = 2.0 * np.sin ( rad*zlam ) * np.tan ( rpi/4. - rad*zphi/2. )\1451 - 2.0 * np.sin ( rad*zlan ) * np.tan ( rpi/4. - rad*zphh/2. )1698 zxffv = 2.0 * np.cos ( RAD*zlam ) * np.tan ( RPI/4. - RAD*zphi/2. ) \ 1699 - 2.0 * np.cos ( RAD*zlan ) * np.tan ( RPI/4. - RAD*zphh/2. ) 1700 zyffv = 2.0 * np.sin ( RAD*zlam ) * np.tan ( RPI/4. - RAD*zphi/2. ) \ 1701 - 2.0 * np.sin ( RAD*zlan ) * np.tan ( RPI/4. - RAD*zphh/2. ) 1452 1702 znffv = np.sqrt ( znnpv * ( zxffv*zxffv + zyffv*zyffv ) ) 1453 1703 1454 1704 # j-direction: u-point segment direction (around f-point) 1455 zlam = np.roll (zlamu, axis=-2, shift=-1) # glamu (ji,jj+1) 1705 zlam = np.roll (zlamu, axis=-2, shift=-1) # glamu (ji,jj+1) 1456 1706 zphi = np.roll (zphiu, axis=-2, shift=-1) # gphiu (ji,jj+1) 1457 1707 zlan = zlamu 1458 1708 zphh = zphiu 1459 zxuuf = 2. * np.cos ( rad*zlam ) * np.tan ( rpi/4. - rad*zphi/2. )\1460 - 2. * np.cos ( rad*zlan ) * np.tan ( rpi/4. - rad*zphh/2. )1461 zyuuf = 2. * np.sin ( rad*zlam ) * np.tan ( rpi/4. - rad*zphi/2. )\1462 - 2. * np.sin ( rad*zlan ) * np.tan ( rpi/4. - rad*zphh/2. )1709 zxuuf = 2. * np.cos ( RAD*zlam ) * np.tan ( RPI/4. - RAD*zphi/2. ) \ 1710 - 2. * np.cos ( RAD*zlan ) * np.tan ( RPI/4. - RAD*zphh/2. ) 1711 zyuuf = 2. * np.sin ( RAD*zlam ) * np.tan ( RPI/4. - RAD*zphi/2. ) \ 1712 - 2. * np.sin ( RAD*zlan ) * np.tan ( RPI/4. - RAD*zphh/2. ) 1463 1713 znuuf = np.sqrt ( znnpf * ( zxuuf*zxuuf + zyuuf*zyuuf ) ) 1464 1714 1465 1715 1466 1716 # cosinus and sinus using scalar and vectorial products 1467 1717 gsint = ( zxnpt*zyvvt - zynpt*zxvvt ) / znvvt 1468 1718 gcost = ( zxnpt*zxvvt + zynpt*zyvvt ) / znvvt 1469 1719 1470 1720 gsinu = ( zxnpu*zyffu - zynpu*zxffu ) / znffu 1471 1721 gcosu = ( zxnpu*zxffu + zynpu*zyffu ) / znffu 1472 1722 1473 1723 gsinf = ( zxnpf*zyuuf - zynpf*zxuuf ) / znuuf 1474 1724 gcosf = ( zxnpf*zxuuf + zynpf*zyuuf ) / znuuf 1475 1725 1476 1726 gsinv = ( zxnpv*zxffv + zynpv*zyffv ) / znffv 1477 gcosv =-( zxnpv*zyffv - zynpv*zxffv ) / znffv # (caution, rotation of 90 degres) 1478 1479 #gsint = lbc (gsint, cd_type='T', nperio=nperio, psgn=-1.) 1480 #gcost = lbc (gcost, cd_type='T', nperio=nperio, psgn=-1.) 1481 #gsinu = lbc (gsinu, cd_type='U', nperio=nperio, psgn=-1.) 1482 #gcosu = lbc (gcosu, cd_type='U', nperio=nperio, psgn=-1.) 1483 #gsinv = lbc (gsinv, cd_type='V', nperio=nperio, psgn=-1.) 1484 #gcosv = lbc (gcosv, cd_type='V', nperio=nperio, psgn=-1.) 1485 #gsinf = lbc (gsinf, cd_type='F', nperio=nperio, psgn=-1.) 1486 #gcosf = lbc (gcosf, cd_type='F', nperio=nperio, psgn=-1.) 1727 # (caution, rotation of 90 degres) 1728 gcosv =-( zxnpv*zyffv - zynpv*zxffv ) / znffv 1487 1729 1488 1730 gsint = lbc_del (gsint, cd_type='T', nperio=nperio, psgn=-1.) … … 1508 1750 1509 1751 def angle (glam, gphi, nperio, cd_type='T') : 1510 '''Compute sinus and cosinus of model line direction with respect to east''' 1752 '''Computes sinus and cosinus of model line direction with 1753 respect to east 1754 ''' 1511 1755 mmath = __mmath__ (glam) 1512 1756 1513 1757 zlam = lbc_add (glam, nperio, cd_type, 1.) 1514 1758 zphi = lbc_add (gphi, nperio, cd_type, 1.) 1515 1759 1516 1760 # north pole direction & modulous 1517 zxnp = 0. - 2.0 * np.cos ( rad*zlam) * np.tan (rpi/4.0 - rad*zphi/2.0)1518 zynp = 0. - 2.0 * np.sin ( rad*zlam) * np.tan (rpi/4.0 - rad*zphi/2.0)1761 zxnp = 0. - 2.0 * np.cos (RAD*zlam) * np.tan (RPI/4.0 - RAD*zphi/2.0) 1762 zynp = 0. - 2.0 * np.sin (RAD*zlam) * np.tan (RPI/4.0 - RAD*zphi/2.0) 1519 1763 znnp = zxnp*zxnp + zynp*zynp 1520 1764 … … 1524 1768 zlan_s = np.roll (zlam, axis=-2, shift= 1) # glam [jj-1, ji] 1525 1769 zphh_s = np.roll (zphi, axis=-2, shift= 1) # gphi [jj-1, ji] 1526 1527 zxff = 2.0 * np.cos ( rad*zlan_n) * np.tan (rpi/4.0 - rad*zphh_n/2.0) \1528 - 2.0 * np.cos ( rad*zlan_s) * np.tan (rpi/4.0 - rad*zphh_s/2.0)1529 zyff = 2.0 * np.sin ( rad*zlan_n) * np.tan (rpi/4.0 - rad*zphh_n/2.0) \1530 - 2.0 * np.sin ( rad*zlan_s) * np.tan (rpi/4.0 - rad*zphh_s/2.0)1770 1771 zxff = 2.0 * np.cos (RAD*zlan_n) * np.tan (RPI/4.0 - RAD*zphh_n/2.0) \ 1772 - 2.0 * np.cos (RAD*zlan_s) * np.tan (RPI/4.0 - RAD*zphh_s/2.0) 1773 zyff = 2.0 * np.sin (RAD*zlan_n) * np.tan (RPI/4.0 - RAD*zphh_n/2.0) \ 1774 - 2.0 * np.sin (RAD*zlan_s) * np.tan (RPI/4.0 - RAD*zphh_s/2.0) 1531 1775 znff = np.sqrt (znnp * (zxff*zxff + zyff*zyff) ) 1532 1776 1533 1777 gsin = (zxnp*zyff - zynp*zxff) / znff 1534 1778 gcos = (zxnp*zxff + zynp*zyff) / znff … … 1540 1784 gsin = gsin.assign_coords ( glam.coords ) 1541 1785 gcos = gcos.assign_coords ( glam.coords ) 1542 1786 1543 1787 return gsin, gcos 1544 1788 1545 1789 def rot_en2ij ( u_e, v_n, gsin, gcos, nperio, cd_type ) : 1546 ''' 1547 ** Purpose : Rotate the Repere: Change vector componantes between 1790 '''Rotates the Repere: Change vector componantes between 1548 1791 geographic grid --> stretched coordinates grid. 1549 All components are on the same grid (T, U, V or F) 1792 1793 All components are on the same grid (T, U, V or F) 1550 1794 ''' 1551 1795 1552 1796 u_i = + u_e * gcos + v_n * gsin 1553 1797 v_j = - u_e * gsin + v_n * gcos 1554 1798 1555 1799 u_i = lbc (u_i, nperio=nperio, cd_type=cd_type, psgn=-1.0) 1556 1800 v_j = lbc (v_j, nperio=nperio, cd_type=cd_type, psgn=-1.0) 1557 1801 1558 1802 return u_i, v_j 1559 1803 1560 1804 def rot_ij2en ( u_i, v_j, gsin, gcos, nperio, cd_type='T' ) : 1561 ''' 1562 ** Purpose : Rotate the Repere: Change vector componantes from 1805 '''Rotates the Repere: Change vector componantes from 1563 1806 stretched coordinates grid --> geographic grid 1564 All components are on the same grid (T, U, V or F) 1807 1808 All components are on the same grid (T, U, V or F) 1565 1809 ''' 1566 1810 u_e = + u_i * gcos - v_j * gsin 1567 1811 v_n = + u_i * gsin + v_j * gcos 1568 1812 1569 1813 u_e = lbc (u_e, nperio=nperio, cd_type=cd_type, psgn=1.0) 1570 1814 v_n = lbc (v_n, nperio=nperio, cd_type=cd_type, psgn=1.0) 1815 1816 return u_e, v_n 1817 1818 def rot_uv2en ( uo, vo, gsint, gcost, nperio, zdim=None ) : 1819 '''Rotate the Repere: Change vector componantes from 1820 stretched coordinates grid --> geographic grid 1821 1822 uo : velocity along i at the U grid point 1823 vo : valocity along j at the V grid point 1571 1824 1572 return u_e, v_n 1573 1574 def rot_uv2en ( uo, vo, gsint, gcost, nperio, zdim=None ) : 1575 ''' 1576 ** Purpose : Rotate the Repere: Change vector componantes from 1577 stretched coordinates grid --> geographic grid 1578 uo is on the U grid point, vo is on the V grid point 1579 east-north components on the T grid point 1580 ''' 1581 mmath = __mmath__ (uo) 1582 1583 ut = U2T (uo, nperio=nperio, psgn=-1.0, zdim=zdim) 1584 vt = V2T (vo, nperio=nperio, psgn=-1.0, zdim=zdim) 1585 1825 Returns east-north components on the T grid point 1826 ''' 1827 ut = u2t (uo, nperio=nperio, psgn=-1.0, zdim=zdim) 1828 vt = v2t (vo, nperio=nperio, psgn=-1.0, zdim=zdim) 1829 1586 1830 u_e = + ut * gcost - vt * gsint 1587 1831 v_n = + ut * gsint + vt * gcost … … 1589 1833 u_e = lbc (u_e, nperio=nperio, cd_type='T', psgn=1.0) 1590 1834 v_n = lbc (v_n, nperio=nperio, cd_type='T', psgn=1.0) 1835 1836 return u_e, v_n 1837 1838 def rot_uv2enf ( uo, vo, gsinf, gcosf, nperio, zdim=None ) : 1839 '''Rotates the Repere: Change vector componantes from 1840 stretched coordinates grid --> geographic grid 1841 1842 uo : velocity along i at the U grid point 1843 vo : valocity along j at the V grid point 1591 1844 1592 return u_e, v_n 1593 1594 def rot_uv2enF ( uo, vo, gsinf, gcosf, nperio, zdim=None ) : 1595 ''' 1596 ** Purpose : Rotate the Repere: Change vector componantes from 1597 stretched coordinates grid --> geographic grid 1598 uo is on the U grid point, vo is on the V grid point 1599 east-north components on the T grid point 1600 ''' 1601 mmath = __mmath__ (uo) 1602 1603 uf = U2F (uo, nperio=nperio, psgn=-1.0, zdim=zdim) 1604 vf = V2F (vo, nperio=nperio, psgn=-1.0, zdim=zdim) 1605 1845 Returns east-north components on the F grid point 1846 ''' 1847 uf = u2f (uo, nperio=nperio, psgn=-1.0, zdim=zdim) 1848 vf = v2f (vo, nperio=nperio, psgn=-1.0, zdim=zdim) 1849 1606 1850 u_e = + uf * gcosf - vf * gsinf 1607 1851 v_n = + uf * gsinf + vf * gcosf … … 1609 1853 u_e = lbc (u_e, nperio=nperio, cd_type='F', psgn= 1.0) 1610 1854 v_n = lbc (v_n, nperio=nperio, cd_type='F', psgn= 1.0) 1611 1855 1612 1856 return u_e, v_n 1613 1857 1614 def U2T (utab, nperio=None, psgn=-1.0, zdim=None, action='ave') : 1615 '''Interpolate an array from U grid to T grid i-mean)''' 1858 def u2t (utab, nperio=None, psgn=-1.0, zdim=None, action='ave') : 1859 '''Interpolates an array from U grid to T grid (i-mean) 1860 ''' 1616 1861 mmath = __mmath__ (utab) 1617 1862 utab_0 = mmath.where ( np.isnan(utab), 0., utab) 1618 lperio, aperio = lbc_diag (nperio)1863 #lperio, aperio = lbc_diag (nperio) 1619 1864 utab_0 = lbc_add (utab_0, nperio=nperio, cd_type='U', psgn=psgn) 1620 ix, ax = __findAxis__ (utab_0, 'x') 1621 kz, az = __findAxis__ (utab_0, 'z') 1622 1623 if ax : 1624 if action == 'ave' : ttab = 0.5 * (utab_0 + np.roll (utab_0, axis=ix, shift=1)) 1625 if action == 'min' : ttab = np.minimum (utab_0 , np.roll (utab_0, axis=ix, shift=1)) 1626 if action == 'max' : ttab = np.maximum (utab_0 , np.roll (utab_0, axis=ix, shift=1)) 1627 if action == 'mult': ttab = utab_0 * np.roll (utab_0, axis=ix, shift=1) 1865 ax, ix = __find_axis__ (utab_0, 'x') 1866 az = __find_axis__ (utab_0, 'z')[0] 1867 1868 if ax : 1869 if action == 'ave' : 1870 ttab = 0.5 * (utab_0 + np.roll (utab_0, axis=ix, shift=1)) 1871 if action == 'min' : 1872 ttab = np.minimum (utab_0 , np.roll (utab_0, axis=ix, shift=1)) 1873 if action == 'max' : 1874 ttab = np.maximum (utab_0 , np.roll (utab_0, axis=ix, shift=1)) 1875 if action == 'mult': 1876 ttab = utab_0 * np.roll (utab_0, axis=ix, shift=1) 1628 1877 ttab = lbc_del (ttab , nperio=nperio, cd_type='T', psgn=psgn) 1629 else : 1878 else : 1630 1879 ttab = lbc_del (utab_0, nperio=nperio, cd_type='T', psgn=psgn) 1631 1880 1632 1881 if mmath == xr : 1633 1882 if ax : 1634 1883 ttab = ttab.assign_coords({ax:np.arange (ttab.shape[ix])+1.}) 1635 1884 if zdim and az : 1636 if az != zdim : ttab = ttab.rename( {az:zdim}) 1885 if az != zdim : 1886 ttab = ttab.rename( {az:zdim}) 1637 1887 return ttab 1638 1888 1639 def V2T (vtab, nperio=None, psgn=-1.0, zdim=None, action='ave') : 1640 '''Interpolate an array from V grid to T grid (j-mean)''' 1889 def v2t (vtab, nperio=None, psgn=-1.0, zdim=None, action='ave') : 1890 '''Interpolates an array from V grid to T grid (j-mean) 1891 ''' 1641 1892 mmath = __mmath__ (vtab) 1642 lperio, aperio = lbc_diag (nperio)1893 #lperio, aperio = lbc_diag (nperio) 1643 1894 vtab_0 = mmath.where ( np.isnan(vtab), 0., vtab) 1644 1895 vtab_0 = lbc_add (vtab_0, nperio=nperio, cd_type='V', psgn=psgn) 1645 jy, ay = __findAxis__ (vtab_0, 'y') 1646 kz, az = __findAxis__ (vtab_0, 'z') 1647 if ay : 1648 if action == 'ave' : ttab = 0.5 * (vtab_0 + np.roll (vtab_0, axis=jy, shift=1)) 1649 if action == 'min' : ttab = np.minimum (vtab_0 , np.roll (vtab_0, axis=jy, shift=1)) 1650 if action == 'max' : ttab = np.maximum (vtab_0 , np.roll (vtab_0, axis=jy, shift=1)) 1651 if action == 'mult' : ttab = vtab_0 * np.roll (vtab_0, axis=jy, shift=1) 1896 ay, jy = __find_axis__ (vtab_0, 'y') 1897 az = __find_axis__ (vtab_0, 'z')[0] 1898 if ay : 1899 if action == 'ave' : 1900 ttab = 0.5 * (vtab_0 + np.roll (vtab_0, axis=jy, shift=1)) 1901 if action == 'min' : 1902 ttab = np.minimum (vtab_0 , np.roll (vtab_0, axis=jy, shift=1)) 1903 if action == 'max' : 1904 ttab = np.maximum (vtab_0 , np.roll (vtab_0, axis=jy, shift=1)) 1905 if action == 'mult' : 1906 ttab = vtab_0 * np.roll (vtab_0, axis=jy, shift=1) 1652 1907 ttab = lbc_del (ttab , nperio=nperio, cd_type='T', psgn=psgn) 1653 1908 else : 1654 1909 ttab = lbc_del (vtab_0, nperio=nperio, cd_type='T', psgn=psgn) 1655 1910 1656 1911 if mmath == xr : 1657 1912 if ay : 1658 1913 ttab = ttab.assign_coords({ay:np.arange(ttab.shape[jy])+1.}) 1659 1914 if zdim and az : 1660 if az != zdim : ttab = ttab.rename( {az:zdim}) 1915 if az != zdim : 1916 ttab = ttab.rename( {az:zdim}) 1661 1917 return ttab 1662 1918 1663 def F2T (ftab, nperio=None, psgn=1.0, zdim=None, action='ave') : 1664 '''Interpolate an array from F grid to T grid (i- and j- means)''' 1919 def f2t (ftab, nperio=None, psgn=1.0, zdim=None, action='ave') : 1920 '''Interpolates an array from F grid to T grid (i- and j- means) 1921 ''' 1665 1922 mmath = __mmath__ (ftab) 1666 1923 ftab_0 = mmath.where ( np.isnan(ftab), 0., ftab) 1667 1924 ftab_0 = lbc_add (ftab_0 , nperio=nperio, cd_type='F', psgn=psgn) 1668 ttab = V2T (F2V (ftab_0, nperio=nperio, psgn=psgn, zdim=zdim, action=action), nperio=nperio, psgn=psgn, zdim=zdim, action=action) 1925 ttab = v2t (f2v (ftab_0, nperio=nperio, psgn=psgn, zdim=zdim, action=action), 1926 nperio=nperio, psgn=psgn, zdim=zdim, action=action) 1669 1927 return lbc_del (ttab, nperio=nperio, cd_type='T', psgn=psgn) 1670 1928 1671 def T2U (ttab, nperio=None, psgn=1.0, zdim=None, action='ave') : 1672 '''Interpolate an array from T grid to U grid (i-mean)''' 1929 def t2u (ttab, nperio=None, psgn=1.0, zdim=None, action='ave') : 1930 '''Interpolates an array from T grid to U grid (i-mean) 1931 ''' 1673 1932 mmath = __mmath__ (ttab) 1674 1933 ttab_0 = mmath.where ( np.isnan(ttab), 0., ttab) 1675 1934 ttab_0 = lbc_add (ttab_0 , nperio=nperio, cd_type='T', psgn=psgn) 1676 ix, ax = __findAxis__ (ttab_0, 'x') 1677 kz, az = __findAxis__ (ttab_0, 'z') 1678 if ix : 1679 if action == 'ave' : utab = 0.5 * (ttab_0 + np.roll (ttab_0, axis=ix, shift=-1)) 1680 if action == 'min' : utab = np.minimum (ttab_0 , np.roll (ttab_0, axis=ix, shift=-1)) 1681 if action == 'max' : utab = np.maximum (ttab_0 , np.roll (ttab_0, axis=ix, shift=-1)) 1682 if action == 'mult' : utab = ttab_0 * np.roll (ttab_0, axis=ix, shift=-1) 1935 ax, ix = __find_axis__ (ttab_0, 'x')[0] 1936 az = __find_axis__ (ttab_0, 'z') 1937 if ix : 1938 if action == 'ave' : 1939 utab = 0.5 * (ttab_0 + np.roll (ttab_0, axis=ix, shift=-1)) 1940 if action == 'min' : 1941 utab = np.minimum (ttab_0 , np.roll (ttab_0, axis=ix, shift=-1)) 1942 if action == 'max' : 1943 utab = np.maximum (ttab_0 , np.roll (ttab_0, axis=ix, shift=-1)) 1944 if action == 'mult' : 1945 utab = ttab_0 * np.roll (ttab_0, axis=ix, shift=-1) 1683 1946 utab = lbc_del (utab , nperio=nperio, cd_type='U', psgn=psgn) 1684 1947 else : 1685 1948 utab = lbc_del (ttab_0, nperio=nperio, cd_type='U', psgn=psgn) 1686 1687 if mmath == xr : 1688 if ax : 1949 1950 if mmath == xr : 1951 if ax : 1689 1952 utab = ttab.assign_coords({ax:np.arange(utab.shape[ix])+1.}) 1690 1953 if zdim and az : 1691 if az != zdim : utab = utab.rename( {az:zdim}) 1954 if az != zdim : 1955 utab = utab.rename( {az:zdim}) 1692 1956 return utab 1693 1957 1694 def T2V (ttab, nperio=None, psgn=1.0, zdim=None, action='ave') : 1695 '''Interpolate an array from T grid to V grid (j-mean)''' 1958 def t2v (ttab, nperio=None, psgn=1.0, zdim=None, action='ave') : 1959 '''Interpolates an array from T grid to V grid (j-mean) 1960 ''' 1696 1961 mmath = __mmath__ (ttab) 1697 1962 ttab_0 = mmath.where ( np.isnan(ttab), 0., ttab) 1698 1963 ttab_0 = lbc_add (ttab_0 , nperio=nperio, cd_type='T', psgn=psgn) 1699 jy, ay = __findAxis__ (ttab_0, 'y') 1700 kz, az = __findAxis__ (ttab_0, 'z') 1701 if jy : 1702 if action == 'ave' : vtab = 0.5 * (ttab_0 + np.roll (ttab_0, axis=jy, shift=-1)) 1703 if action == 'min' : vtab = np.minimum (ttab_0 , np.roll (ttab_0, axis=jy, shift=-1)) 1704 if action == 'max' : vtab = np.maximum (ttab_0 , np.roll (ttab_0, axis=jy, shift=-1)) 1705 if action == 'mult' : vtab = ttab_0 * np.roll (ttab_0, axis=jy, shift=-1) 1964 ay, jy = __find_axis__ (ttab_0, 'y') 1965 az = __find_axis__ (ttab_0, 'z')[0] 1966 if jy : 1967 if action == 'ave' : 1968 vtab = 0.5 * (ttab_0 + np.roll (ttab_0, axis=jy, shift=-1)) 1969 if action == 'min' : 1970 vtab = np.minimum (ttab_0 , np.roll (ttab_0, axis=jy, shift=-1)) 1971 if action == 'max' : 1972 vtab = np.maximum (ttab_0 , np.roll (ttab_0, axis=jy, shift=-1)) 1973 if action == 'mult' : 1974 vtab = ttab_0 * np.roll (ttab_0, axis=jy, shift=-1) 1706 1975 vtab = lbc_del (vtab , nperio=nperio, cd_type='V', psgn=psgn) 1707 1976 else : … … 1709 1978 1710 1979 if mmath == xr : 1711 if ay : 1980 if ay : 1712 1981 vtab = vtab.assign_coords({ay:np.arange(vtab.shape[jy])+1.}) 1713 1982 if zdim and az : 1714 if az != zdim : vtab = vtab.rename( {az:zdim}) 1983 if az != zdim : 1984 vtab = vtab.rename( {az:zdim}) 1715 1985 return vtab 1716 1986 1717 def V2F (vtab, nperio=None, psgn=-1.0, zdim=None, action='ave') : 1718 '''Interpolate an array from V grid to F grid (i-mean)''' 1987 def v2f (vtab, nperio=None, psgn=-1.0, zdim=None, action='ave') : 1988 '''Interpolates an array from V grid to F grid (i-mean) 1989 ''' 1719 1990 mmath = __mmath__ (vtab) 1720 1991 vtab_0 = mmath.where ( np.isnan(vtab), 0., vtab) 1721 1992 vtab_0 = lbc_add (vtab_0 , nperio=nperio, cd_type='V', psgn=psgn) 1722 ix, ax = __findAxis__ (vtab_0, 'x') 1723 kz, az = __findAxis__ (vtab_0, 'z') 1724 if ix : 1725 if action == 'ave' : 0.5 * (vtab_0 + np.roll (vtab_0, axis=ix, shift=-1)) 1726 if action == 'min' : np.minimum (vtab_0 , np.roll (vtab_0, axis=ix, shift=-1)) 1727 if action == 'max' : np.maximum (vtab_0 , np.roll (vtab_0, axis=ix, shift=-1)) 1728 if action == 'mult' : vtab_0 * np.roll (vtab_0, axis=ix, shift=-1) 1993 ax, ix = __find_axis__ (vtab_0, 'x') 1994 az = __find_axis__ (vtab_0, 'z')[0] 1995 if ix : 1996 if action == 'ave' : 1997 ftab = 0.5 * (vtab_0 + np.roll (vtab_0, axis=ix, shift=-1)) 1998 if action == 'min' : 1999 ftab = np.minimum (vtab_0 , np.roll (vtab_0, axis=ix, shift=-1)) 2000 if action == 'max' : 2001 ftab = np.maximum (vtab_0 , np.roll (vtab_0, axis=ix, shift=-1)) 2002 if action == 'mult' : 2003 ftab = vtab_0 * np.roll (vtab_0, axis=ix, shift=-1) 1729 2004 ftab = lbc_del (ftab , nperio=nperio, cd_type='F', psgn=psgn) 1730 2005 else : 1731 1732 2006 ftab = lbc_del (vtab_0, nperio=nperio, cd_type='F', psgn=psgn) 2007 1733 2008 if mmath == xr : 1734 if ax : 2009 if ax : 1735 2010 ftab = ftab.assign_coords({ax:np.arange(ftab.shape[ix])+1.}) 1736 2011 if zdim and az : 1737 if az != zdim : ftab = ftab.rename( {az:zdim}) 2012 if az != zdim : 2013 ftab = ftab.rename( {az:zdim}) 1738 2014 return lbc_del (ftab, nperio=nperio, cd_type='F', psgn=psgn) 1739 2015 1740 def U2F (utab, nperio=None, psgn=-1.0, zdim=None, action='ave') : 1741 '''Interpolate an array from U grid to F grid i-mean)''' 2016 def u2f (utab, nperio=None, psgn=-1.0, zdim=None, action='ave') : 2017 '''Interpolates an array from U grid to F grid i-mean) 2018 ''' 1742 2019 mmath = __mmath__ (utab) 1743 2020 utab_0 = mmath.where ( np.isnan(utab), 0., utab) 1744 2021 utab_0 = lbc_add (utab_0 , nperio=nperio, cd_type='U', psgn=psgn) 1745 jy, ay = __findAxis__ (utab_0, 'y') 1746 kz, az = __findAxis__ (utab_0, 'z') 1747 if jy : 1748 if action == 'ave' : ftab = 0.5 * (utab_0 + np.roll (utab_0, axis=jy, shift=-1)) 1749 if action == 'min' : ftab = np.minimum (utab_0 , np.roll (utab_0, axis=jy, shift=-1)) 1750 if action == 'max' : ftab = np.maximum (utab_0 , np.roll (utab_0, axis=jy, shift=-1)) 1751 if action == 'mult' : ftab = utab_0 * np.roll (utab_0, axis=jy, shift=-1) 1752 ftab = lbc_del (ftab , nperio=nperio, cd_type='F', psgn=psgn) 2022 ay, jy = __find_axis__ (utab_0, 'y') 2023 az = __find_axis__ (utab_0, 'z')[0] 2024 if jy : 2025 if action == 'ave' : 2026 ftab = 0.5 * (utab_0 + np.roll (utab_0, axis=jy, shift=-1)) 2027 if action == 'min' : 2028 ftab = np.minimum (utab_0 , np.roll (utab_0, axis=jy, shift=-1)) 2029 if action == 'max' : 2030 ftab = np.maximum (utab_0 , np.roll (utab_0, axis=jy, shift=-1)) 2031 if action == 'mult' : 2032 ftab = utab_0 * np.roll (utab_0, axis=jy, shift=-1) 2033 ftab = lbc_del (ftab, nperio=nperio, cd_type='F', psgn=psgn) 1753 2034 else : 1754 2035 ftab = lbc_del (utab_0, nperio=nperio, cd_type='F', psgn=psgn) 1755 2036 1756 2037 if mmath == xr : 1757 if ay : 2038 if ay : 1758 2039 ftab = ftab.assign_coords({'y':np.arange(ftab.shape[jy])+1.}) 1759 2040 if zdim and az : 1760 if az != zdim : ftab = ftab.rename( {az:zdim}) 2041 if az != zdim : 2042 ftab = ftab.rename( {az:zdim}) 1761 2043 return ftab 1762 2044 1763 def F2T (ftab, nperio=None, psgn=1.0, zdim=None, action='ave') : 1764 '''Interpolate an array on F grid to T grid (i- and j- means)''' 1765 mmath = __mmath__ (ftab) 1766 ftab_0 = mmath.where ( np.isnan(ttab), 0., ttab) 1767 ftab_0 = lbc_add (ftab_0 , nperio=nperio, cd_type='F', psgn=psgn) 1768 ttab = U2T(F2U(ftab_0, nperio=nperio, psgn=psgn, zdim=zdim, action=action), nperio=nperio, psgn=psgn, zdim=zdim, action=action) 1769 return lbc_del (ttab, nperio=nperio, cd_type='T', psgn=psgn) 1770 1771 def T2F (ttab, nperio=None, psgn=1.0, zdim=None, action='mean') : 1772 '''Interpolate an array on T grid to F grid (i- and j- means)''' 2045 def t2f (ttab, nperio=None, psgn=1.0, zdim=None, action='mean') : 2046 '''Interpolates an array on T grid to F grid (i- and j- means) 2047 ''' 1773 2048 mmath = __mmath__ (ttab) 1774 2049 ttab_0 = mmath.where ( np.isnan(ttab), 0., ttab) 1775 2050 ttab_0 = lbc_add (ttab_0 , nperio=nperio, cd_type='T', psgn=psgn) 1776 ftab = T2U (U2F (ttab, nperio=nperio, psgn=psgn, zdim=zdim, action=action), nperio=nperio, psgn=psgn, zdim=zdim, action=action) 1777 2051 ftab = t2u (u2f (ttab, nperio=nperio, psgn=psgn, zdim=zdim, action=action), 2052 nperio=nperio, psgn=psgn, zdim=zdim, action=action) 2053 1778 2054 return lbc_del (ftab, nperio=nperio, cd_type='F', psgn=psgn) 1779 2055 1780 def F2U (ftab, nperio=None, psgn=1.0, zdim=None, action='ave') : 1781 '''Interpolate an array on F grid to FUgrid (i-mean)''' 2056 def f2u (ftab, nperio=None, psgn=1.0, zdim=None, action='ave') : 2057 '''Interpolates an array on F grid to U grid (j-mean) 2058 ''' 1782 2059 mmath = __mmath__ (ftab) 1783 2060 ftab_0 = mmath.where ( np.isnan(ftab), 0., ftab) 1784 2061 ftab_0 = lbc_add (ftab_0 , nperio=nperio, cd_type='F', psgn=psgn) 1785 jy, ay = __findAxis__ (ftab_0, 'y') 1786 kz, az = __findAxis__ (ftab_0, 'z') 1787 if jy : 1788 if action == 'ave' : utab = 0.5 * (ftab_0 + np.roll (ftab_0, axis=jy, shift=-1)) 1789 if action == 'min' : utab = np.minimum (ftab_0 , np.roll (ftab_0, axis=jy, shift=-1)) 1790 if action == 'max' : utab = np.maximum (ftab_0 , np.roll (ftab_0, axis=jy, shift=-1)) 1791 if action == 'mult' : utab = ftab_0 * np.roll (ftab_0, axis=jy, shift=-1) 2062 ay, jy = __find_axis__ (ftab_0, 'y') 2063 az = __find_axis__ (ftab_0, 'z')[0] 2064 if jy : 2065 if action == 'ave' : 2066 utab = 0.5 * (ftab_0 + np.roll (ftab_0, axis=jy, shift=-1)) 2067 if action == 'min' : 2068 utab = np.minimum (ftab_0 , np.roll (ftab_0, axis=jy, shift=-1)) 2069 if action == 'max' : 2070 utab = np.maximum (ftab_0 , np.roll (ftab_0, axis=jy, shift=-1)) 2071 if action == 'mult' : 2072 utab = ftab_0 * np.roll (ftab_0, axis=jy, shift=-1) 1792 2073 utab = lbc_del (utab , nperio=nperio, cd_type='U', psgn=psgn) 1793 2074 else : … … 1796 2077 if mmath == xr : 1797 2078 utab = utab.assign_coords({ay:np.arange(ftab.shape[jy])+1.}) 1798 if zdim and zz:1799 if az != zdim : utab = utab.rename( {az:zdim})2079 if zdim and az and az != zdim : 2080 utab = utab.rename( {az:zdim}) 1800 2081 return utab 1801 2082 1802 def F2V (ftab, nperio=None, psgn=1.0, zdim=None, action='ave') : 1803 '''Interpolate an array from F grid to V grid (i-mean)''' 2083 def f2v (ftab, nperio=None, psgn=1.0, zdim=None, action='ave') : 2084 '''Interpolates an array from F grid to V grid (i-mean) 2085 ''' 1804 2086 mmath = __mmath__ (ftab) 1805 2087 ftab_0 = mmath.where ( np.isnan(ftab), 0., ftab) 1806 2088 ftab_0 = lbc_add (ftab_0 , nperio=nperio, cd_type='F', psgn=psgn) 1807 ix, ax = __findAxis__ (ftab_0, 'x') 1808 kz, az = __findAxis__ (ftab_0, 'z') 1809 if ix : 1810 if action == 'ave' : vtab = 0.5 * (ftab_0 + np.roll (ftab_0, axis=ix, shift=-1)) 1811 if action == 'min' : vtab = np.minimum (ftab_0 , np.roll (ftab_0, axis=ix, shift=-1)) 1812 if action == 'max' : vtab = np.maximum (ftab_0 , np.roll (ftab_0, axis=ix, shift=-1)) 1813 if action == 'mult' : vtab = ftab_0 * np.roll (ftab_0, axis=ix, shift=-1) 2089 ax, ix = __find_axis__ (ftab_0, 'x') 2090 az = __find_axis__ (ftab_0, 'z')[0] 2091 if ix : 2092 if action == 'ave' : 2093 vtab = 0.5 * (ftab_0 + np.roll (ftab_0, axis=ix, shift=-1)) 2094 if action == 'min' : 2095 vtab = np.minimum (ftab_0 , np.roll (ftab_0, axis=ix, shift=-1)) 2096 if action == 'max' : 2097 vtab = np.maximum (ftab_0 , np.roll (ftab_0, axis=ix, shift=-1)) 2098 if action == 'mult' : 2099 vtab = ftab_0 * np.roll (ftab_0, axis=ix, shift=-1) 1814 2100 vtab = lbc_del (vtab , nperio=nperio, cd_type='V', psgn=psgn) 1815 else : 2101 else : 1816 2102 vtab = lbc_del (ftab_0, nperio=nperio, cd_type='V', psgn=psgn) 1817 2103 … … 1819 2105 vtab = vtab.assign_coords({ax:np.arange(ftab.shape[ix])+1.}) 1820 2106 if zdim and az : 1821 if az != zdim : vtab = vtab.rename( {az:zdim}) 2107 if az != zdim : 2108 vtab = vtab.rename( {az:zdim}) 1822 2109 return vtab 1823 2110 1824 def W2T(wtab, zcoord=None, zdim=None, sval=np.nan) :1825 ''' 1826 Interpolate an array on W grid to T grid (k-mean) 2111 def w2t (wtab, zcoord=None, zdim=None, sval=np.nan) : 2112 '''Interpolates an array on W grid to T grid (k-mean) 2113 1827 2114 sval is the bottom value 1828 2115 ''' … … 1830 2117 wtab_0 = mmath.where ( np.isnan(wtab), 0., wtab) 1831 2118 1832 kz, az = __findAxis__ (wtab_0, 'z')1833 1834 if kz : 2119 az, kz = __find_axis__ (wtab_0, 'z') 2120 2121 if kz : 1835 2122 ttab = 0.5 * ( wtab_0 + np.roll (wtab_0, axis=kz, shift=-1) ) 1836 2123 else : … … 1840 2127 ttab[{az:kz}] = sval 1841 2128 if zdim and az : 1842 if az != zdim : ttab = ttab.rename ( {az:zdim} ) 2129 if az != zdim : 2130 ttab = ttab.rename ( {az:zdim} ) 1843 2131 if zcoord is not None : 1844 2132 ttab = ttab.assign_coords ( {zdim:zcoord} ) … … 1848 2136 return ttab 1849 2137 1850 def T2W (ttab, zcoord=None, zdim=None, sval=np.nan, extrap_surf=False) : 1851 '''Interpolate an array from T grid to W grid (k-mean) 2138 def t2w (ttab, zcoord=None, zdim=None, sval=np.nan, extrap_surf=False) : 2139 '''Interpolates an array from T grid to W grid (k-mean) 2140 1852 2141 sval is the surface value 1853 2142 if extrap_surf==True, surface value is taken from 1st level value. … … 1855 2144 mmath = __mmath__ (ttab) 1856 2145 ttab_0 = mmath.where ( np.isnan(ttab), 0., ttab) 1857 kz, az = __findAxis__ (ttab_0, 'z')2146 az, kz = __find_axis__ (ttab_0, 'z') 1858 2147 wtab = 0.5 * ( ttab_0 + np.roll (ttab_0, axis=kz, shift=1) ) 1859 2148 1860 2149 if mmath == xr : 1861 if extrap_surf : wtab[{az:0}] = ttabb[{az:0}] 1862 else : wtab[{az:0}] = sval 1863 else : 1864 if extrap_surf : wtab[..., 0, :, :] = ttab[..., 0, :, :] 1865 else : wtab[..., 0, :, :] = sval 2150 if extrap_surf : 2151 wtab[{az:0}] = ttab[{az:0}] 2152 else : 2153 wtab[{az:0}] = sval 2154 else : 2155 if extrap_surf : 2156 wtab[..., 0, :, :] = ttab[..., 0, :, :] 2157 else : 2158 wtab[..., 0, :, :] = sval 1866 2159 1867 2160 if mmath == xr : 1868 if zdim and az :1869 if az != zdim :wtab = wtab.rename ( {az:zdim})2161 if zdim and az and az != zdim : 2162 wtab = wtab.rename ( {az:zdim}) 1870 2163 if zcoord is not None : 1871 2164 wtab = wtab.assign_coords ( {zdim:zcoord}) 1872 2165 else : 1873 ztab = wtab.assign_coords ( {zdim:np.arange(ttab.shape[kz])+1.} )2166 wtab = wtab.assign_coords ( {zdim:np.arange(ttab.shape[kz])+1.} ) 1874 2167 return wtab 1875 2168 1876 def fill (ptab, nperio, cd_type='T', npass=1, sval=0.) : 1877 ''' 1878 Fill sval values with mean of neighbours 1879 2169 def fill (ptab, nperio, cd_type='T', npass=1, sval=np.nan) : 2170 '''Fills np.nan values with mean of neighbours 2171 1880 2172 Inputs : 1881 2173 ptab : input field to fill 1882 2174 nperio, cd_type : periodicity characteristics 1883 ''' 2175 ''' 1884 2176 1885 2177 mmath = __mmath__ (ptab) 1886 2178 1887 DoPerio = False ; lperio = nperio 2179 do_perio = False 2180 lperio = nperio 1888 2181 if nperio == 4.2 : 1889 DoPerio = True ; lperio =42182 do_perio, lperio = True, 4 1890 2183 if nperio == 6.2 : 1891 DoPerio = True ; lperio =61892 1893 if DoPerio :1894 ztab = lbc_add (ptab, nperio=nperio , sval=sval)1895 else : 2184 do_perio, lperio = True, 6 2185 2186 if do_perio : 2187 ztab = lbc_add (ptab, nperio=nperio) 2188 else : 1896 2189 ztab = ptab 1897 1898 if np.isnan (sval) : 2190 2191 if np.isnan (sval) : 1899 2192 ztab = mmath.where (np.isnan(ztab), np.nan, ztab) 1900 2193 else : 1901 2194 ztab = mmath.where (ztab==sval , np.nan, ztab) 1902 1903 for nn in np.arange (npass) :2195 2196 for _ in np.arange (npass) : 1904 2197 zmask = mmath.where ( np.isnan(ztab), 0., 1. ) 1905 2198 ztab0 = mmath.where ( np.isnan(ztab), 0., ztab ) … … 1925 2218 zcount = lbc (zcount, nperio=lperio, cd_type=cd_type) 1926 2219 znew = lbc (znew , nperio=lperio, cd_type=cd_type) 1927 2220 1928 2221 ztab = mmath.where (np.logical_and (zmask==0., zcount>0), znew/zcount, ztab) 1929 2222 1930 2223 ztab = mmath.where (zcount==0, sval, ztab) 1931 if DoPerio : ztab = lbc_del (ztab, nperio=lperio) 2224 if do_perio : 2225 ztab = lbc_del (ztab, nperio=lperio) 1932 2226 1933 2227 return ztab … … 1935 2229 def correct_uv (u, v, lat) : 1936 2230 ''' 1937 Correct a Cartopy bug in orthographic projection2231 Corrects a Cartopy bug in orthographic projection 1938 2232 1939 2233 See https://github.com/SciTools/cartopy/issues/1179 … … 1947 2241 lat : latitude of the point (degrees north) 1948 2242 1949 Outputs : 2243 Outputs : 1950 2244 modified eastward/nothward components to have correct polar projections in cartopy 1951 2245 ''' 1952 2246 uv = np.sqrt (u*u + v*v) # Original modulus 1953 2247 zu = u 1954 zv = v * np.cos ( rad*lat)2248 zv = v * np.cos (RAD*lat) 1955 2249 zz = np.sqrt ( zu*zu + zv*zv ) # Corrected modulus 1956 uc = zu*uv/zz ; vc = zv*uv/zz # Final corrected values 2250 uc = zu*uv/zz 2251 vc = zv*uv/zz # Final corrected values 1957 2252 return uc, vc 1958 2253 1959 2254 def norm_uv (u, v) : 1960 ''' 1961 Return norm of a 2 components vector 2255 '''Returns norm of a 2 components vector 1962 2256 ''' 1963 2257 return np.sqrt (u*u + v*v) 1964 2258 1965 2259 def normalize_uv (u, v) : 1966 ''' 1967 Normalize 2 components vector 2260 '''Normalizes 2 components vector 1968 2261 ''' 1969 2262 uv = norm_uv (u, v) 1970 2263 return u/uv, v/uv 1971 2264 1972 def msf (vv, e1v_e3v, lat1d, depthw) : 1973 ''' 1974 Computes the meridonal stream function 1975 First input is meridional_velocity*e1v*e3v 1976 ''' 1977 2265 def msf (vv, e1v_e3v, plat1d, depthw) : 2266 '''Computes the meridonal stream function 2267 2268 vv : meridional_velocity 2269 e1v_e3v : prodcut of scale factors e1v*e3v 2270 ''' 2271 1978 2272 v_e1v_e3v = vv * e1v_e3v 1979 2273 v_e1v_e3v.attrs = vv.attrs 1980 1981 ix, ax = __findAxis__ (v_e1v_e3v, 'x') 1982 kz, az = __findAxis__ (v_e1v_e3v, 'z') 1983 if az == 'olevel' : new_az = 'olevel' 1984 else : new_az = 'depthw' 2274 2275 ax = __find_axis__ (v_e1v_e3v, 'x')[0] 2276 az = __find_axis__ (v_e1v_e3v, 'z')[0] 2277 if az == 'olevel' : 2278 new_az = 'olevel' 2279 else : 2280 new_az = 'depthw' 1985 2281 1986 2282 zomsf = -v_e1v_e3v.cumsum ( dim=az, keep_attrs=True).sum (dim=ax, keep_attrs=True)*1.E-6 1987 2283 zomsf = zomsf - zomsf.isel ( { az:-1} ) 1988 1989 jy, ay = __findAxis__ (zomsf, 'y' )1990 zomsf = zomsf.assign_coords ( {az:depthw.values, ay: lat1d.values})1991 2284 2285 ay = __find_axis__ (zomsf, 'y' )[0] 2286 zomsf = zomsf.assign_coords ( {az:depthw.values, ay:plat1d.values}) 2287 1992 2288 zomsf = zomsf.rename ( {ay:'lat'}) 1993 if az != new_az : zomsf = zomsf.rename ( {az:new_az} ) 2289 if az != new_az : 2290 zomsf = zomsf.rename ( {az:new_az} ) 1994 2291 zomsf.attrs['standard_name'] = 'Meridional stream function' 1995 2292 zomsf.attrs['long_name'] = 'Meridional stream function' 1996 2293 zomsf.attrs['units'] = 'Sv' 1997 2294 zomsf[new_az].attrs = depthw.attrs 1998 zomsf.lat.attrs= lat1d.attrs1999 2295 zomsf.lat.attrs=plat1d.attrs 2296 2000 2297 return zomsf 2001 2298 2002 2299 def bsf (uu, e2u_e3u, mask, nperio=None, bsf0=None ) : 2003 ''' 2004 Computes the barotropic stream function 2005 First input is zonal_velocity*e2u*e3u 2006 bsf0 is the point with bsf=0 2007 (ex: bsf0={'x':3, 'y':120} for orca2, 2008 bsf0={'x':5, 'y':300} for eeORCA1 2300 '''Computes the barotropic stream function 2301 2302 uu : zonal_velocity 2303 e2u_e3u : product of scales factor e2u*e3u 2304 bsf0 : the point with bsf=0 2305 (ex: bsf0={'x':3, 'y':120} for orca2, 2306 bsf0={'x':5, 'y':300} for eORCA1 2009 2307 ''' 2010 2308 u_e2u_e3u = uu * e2u_e3u 2011 2309 u_e2u_e3u.attrs = uu.attrs 2012 2310 2013 iy, ay = __findAxis__ (u_e2u_e3u, 'y')2014 kz, az = __findAxis__ (u_e2u_e3u, 'z')2015 2016 bsf = -u_e2u_e3u.cumsum ( dim=ay, keep_attrs=True )2017 bsf =bsf.sum (dim=az, keep_attrs=True)*1.E-62018 2311 ay = __find_axis__ (u_e2u_e3u, 'y')[0] 2312 az = __find_axis__ (u_e2u_e3u, 'z')[0] 2313 2314 zbsf = -u_e2u_e3u.cumsum ( dim=ay, keep_attrs=True ) 2315 zbsf = zbsf.sum (dim=az, keep_attrs=True)*1.E-6 2316 2019 2317 if bsf0 : 2020 bsf = bsf - bsf.isel (bsf0) 2021 2022 bsf = bsf.where (mask !=0, np.nan) 2023 for attr in uu.attrs : 2024 bsf.attrs[attr] = uu.attrs[attr] 2025 bsf.attrs['standard_name'] = 'barotropic_stream_function' 2026 bsf.attrs['long_name'] = 'Barotropic stream function' 2027 bsf.attrs['units'] = 'Sv' 2028 bsf = lbc (bsf, nperio=nperio, cd_type='F') 2029 2030 return bsf 2031 2032 def namelist_read (ref=None, cfg=None, out='dict', flat=False, verbose=False) : 2033 ''' 2034 Read NEMO namelist(s) and return either a dictionnary or an xarray dataset 2035 2036 ref : file with reference namelist, or a f90nml.namelist.Namelist object 2037 cfg : file with config namelist, or a f90nml.namelist.Namelist object 2038 At least one namelist neaded 2039 2040 out: 2318 zbsf = zbsf - zbsf.isel (bsf0) 2319 2320 zbsf = zbsf.where (mask !=0, np.nan) 2321 zbsf.attrs.update (uu.attrs) 2322 zbsf.attrs['standard_name'] = 'barotropic_stream_function' 2323 zbsf.attrs['long_name'] = 'Barotropic stream function' 2324 zbsf.attrs['units'] = 'Sv' 2325 zbsf = lbc (zbsf, nperio=nperio, cd_type='F') 2326 2327 return zbsf 2328 2329 if f90nml : 2330 def namelist_read (ref=None, cfg=None, out='dict', flat=False, verbose=False) : 2331 '''Read NEMO namelist(s) and return either a dictionnary or an xarray dataset 2332 2333 ref : file with reference namelist, or a f90nml.namelist.Namelist object 2334 cfg : file with config namelist, or a f90nml.namelist.Namelist object 2335 At least one namelist neaded 2336 2337 out: 2041 2338 'dict' to return a dictonnary 2042 2339 'xr' to return an xarray dataset 2043 flat : only for dict output. Output a flat dictionnary with all values. 2044 2045 ''' 2046 2047 import f90nml 2048 if ref : 2049 if isinstance (ref, str) : nml_ref = f90nml.read (ref) 2050 if isinstance (ref, f90nml.namelist.Namelist) : nml_ref = ref 2051 2052 if cfg : 2053 if isinstance (cfg, str) : nml_cfg = f90nml.read (cfg) 2054 if isinstance (cfg, f90nml.namelist.Namelist) : nml_cfg = cfg 2055 2056 if out == 'dict' : dict_namelist = {} 2057 if out == 'xr' : xr_namelist = xr.Dataset () 2058 2059 list_nml = [] ; list_comment = [] 2060 2061 if ref : list_nml.append (nml_ref) ; list_comment.append ('ref') 2062 if cfg : list_nml.append (nml_cfg) ; list_comment.append ('cfg') 2063 2064 for nml, comment in zip (list_nml, list_comment) : 2065 if verbose : print (comment) 2066 if flat and out =='dict' : 2067 for nam in nml.keys () : 2068 if verbose : print (nam) 2069 for value in nml[nam] : 2070 if out == 'dict' : dict_namelist[value] = nml[nam][value] 2071 if verbose : print (nam, ':', value, ':', nml[nam][value]) 2072 else : 2073 for nam in nml.keys () : 2074 if verbose : print (nam) 2075 if out == 'dict' : 2076 if nam not in dict_namelist.keys () : dict_namelist[nam] = {} 2077 for value in nml[nam] : 2078 if out == 'dict' : dict_namelist[nam][value] = nml[nam][value] 2079 if out == 'xr' : xr_namelist[value] = nml[nam][value] 2080 if verbose : print (nam, ':', value, ':', nml[nam][value]) 2081 2082 if out == 'dict' : return dict_namelist 2083 if out == 'xr' : return xr_namelist 2340 flat : only for dict output. Output a flat dictionary with all values. 2341 2342 ''' 2343 if ref : 2344 if isinstance (ref, str) : 2345 nml_ref = f90nml.read (ref) 2346 if isinstance (ref, f90nml.namelist.Namelist) : 2347 nml_ref = ref 2348 2349 if cfg : 2350 if isinstance (cfg, str) : 2351 nml_cfg = f90nml.read (cfg) 2352 if isinstance (cfg, f90nml.namelist.Namelist) : 2353 nml_cfg = cfg 2354 2355 if out == 'dict' : 2356 dict_namelist = {} 2357 if out == 'xr' : 2358 xr_namelist = xr.Dataset () 2359 2360 list_nml = [] 2361 list_comment = [] 2362 2363 if ref : 2364 list_nml.append (nml_ref) 2365 list_comment.append ('ref') 2366 if cfg : 2367 list_nml.append (nml_cfg) 2368 list_comment.append ('cfg') 2369 2370 for nml, comment in zip (list_nml, list_comment) : 2371 if verbose : 2372 print (comment) 2373 if flat and out =='dict' : 2374 for nam in nml.keys () : 2375 if verbose : 2376 print (nam) 2377 for value in nml[nam] : 2378 if out == 'dict' : 2379 dict_namelist[value] = nml[nam][value] 2380 if verbose : 2381 print (nam, ':', value, ':', nml[nam][value]) 2382 else : 2383 for nam in nml.keys () : 2384 if verbose : 2385 print (nam) 2386 if out == 'dict' : 2387 if nam not in dict_namelist.keys () : 2388 dict_namelist[nam] = {} 2389 for value in nml[nam] : 2390 if out == 'dict' : 2391 dict_namelist[nam][value] = nml[nam][value] 2392 if out == 'xr' : 2393 xr_namelist[value] = nml[nam][value] 2394 if verbose : 2395 print (nam, ':', value, ':', nml[nam][value]) 2396 2397 if out == 'dict' : 2398 return dict_namelist 2399 if out == 'xr' : 2400 return xr_namelist 2401 else : 2402 def namelist_read (ref=None, cfg=None, out='dict', flat=False, verbose=False) : 2403 '''Shadow version of namelist read, when f90nm module was not found 2404 2405 namelist_read : 2406 Read NEMO namelist(s) and return either a dictionnary or an xarray dataset 2407 ''' 2408 print ( 'Error : module f90nml not found' ) 2409 print ( 'Cannot call namelist_read' ) 2410 print ( 'Call parameters where : ') 2411 print ( f'{err=} {ref=} {cfg=} {out=} {flat=} {verbose=}' ) 2084 2412 2085 2413 def fill_closed_seas (imask, nperio=None, cd_type='T') : 2086 2414 '''Fill closed seas with image processing library 2415 2087 2416 imask : mask, 1 on ocean, 0 on land 2088 2417 ''' … … 2094 2423 return imask_filled 2095 2424 2096 ''' 2097 Sea water state function parameters from NEMO code 2098 ''' 2099 rdeltaS = 32. ; r1_S0 = 0.875/35.16504 ; r1_T0 = 1./40. ; r1_Z0 = 1.e-4 2100 2101 EOS000 = 8.0189615746e+02 ; EOS100 = 8.6672408165e+02 ; EOS200 = -1.7864682637e+03 ; EOS300 = 2.0375295546e+03 ; EOS400 = -1.2849161071e+03 ; EOS500 = 4.3227585684e+02 ; EOS600 = -6.0579916612e+01 2102 EOS010 = 2.6010145068e+01 ; EOS110 = -6.5281885265e+01 ; EOS210 = 8.1770425108e+01 ; EOS310 = -5.6888046321e+01 ; EOS410 = 1.7681814114e+01 ; EOS510 = -1.9193502195 2103 EOS020 = -3.7074170417e+01 ; EOS120 = 6.1548258127e+01 ; EOS220 = -6.0362551501e+01 ; EOS320 = 2.9130021253e+01 ; EOS420 = -5.4723692739 ; EOS030 = 2.1661789529e+01 2104 EOS130 = -3.3449108469e+01 ; EOS230 = 1.9717078466e+01 ; EOS330 = -3.1742946532 2105 EOS040 = -8.3627885467 ; EOS140 = 1.1311538584e+01 ; EOS240 = -5.3563304045 2106 EOS050 = 5.4048723791e-01 ; EOS150 = 4.8169980163e-01 2425 # ====================================================== 2426 # Sea water state function parameters from NEMO code 2427 2428 RDELTAS = 32. 2429 R1_S0 = 0.875/35.16504 2430 R1_T0 = 1./40. 2431 R1_Z0 = 1.e-4 2432 2433 EOS000 = 8.0189615746e+02 2434 EOS100 = 8.6672408165e+02 2435 EOS200 = -1.7864682637e+03 2436 EOS300 = 2.0375295546e+03 2437 EOS400 = -1.2849161071e+03 2438 EOS500 = 4.3227585684e+02 2439 EOS600 = -6.0579916612e+01 2440 EOS010 = 2.6010145068e+01 2441 EOS110 = -6.5281885265e+01 2442 EOS210 = 8.1770425108e+01 2443 EOS310 = -5.6888046321e+01 2444 EOS410 = 1.7681814114e+01 2445 EOS510 = -1.9193502195 2446 EOS020 = -3.7074170417e+01 2447 EOS120 = 6.1548258127e+01 2448 EOS220 = -6.0362551501e+01 2449 EOS320 = 2.9130021253e+01 2450 EOS420 = -5.4723692739 2451 EOS030 = 2.1661789529e+01 2452 EOS130 = -3.3449108469e+01 2453 EOS230 = 1.9717078466e+01 2454 EOS330 = -3.1742946532 2455 EOS040 = -8.3627885467 2456 EOS140 = 1.1311538584e+01 2457 EOS240 = -5.3563304045 2458 EOS050 = 5.4048723791e-01 2459 EOS150 = 4.8169980163e-01 2107 2460 EOS060 = -1.9083568888e-01 2108 EOS001 = 1.9681925209e+01 ; EOS101 = -4.2549998214e+01 ; EOS201 = 5.0774768218e+01 ; EOS301 = -3.0938076334e+01 ; EOS401 = 6.6051753097 ; EOS011 = -1.3336301113e+01 2109 EOS111 = -4.4870114575 ; EOS211 = 5.0042598061 ; EOS311 = -6.5399043664e-01 ; EOS021 = 6.7080479603 ; EOS121 = 3.5063081279 2110 EOS221 = -1.8795372996 ; EOS031 = -2.4649669534 ; EOS131 = -5.5077101279e-01 ; EOS041 = 5.5927935970e-01 2111 EOS002 = 2.0660924175 ; EOS102 = -4.9527603989 ; EOS202 = 2.5019633244 ; EOS012 = 2.0564311499 ; EOS112 = -2.1311365518e-01 ; EOS022 = -1.2419983026 2112 EOS003 = -2.3342758797e-02 ; EOS103 = -1.8507636718e-02 ; EOS013 = 3.7969820455e-01 2461 EOS001 = 1.9681925209e+01 2462 EOS101 = -4.2549998214e+01 2463 EOS201 = 5.0774768218e+01 2464 EOS301 = -3.0938076334e+01 2465 EOS401 = 6.6051753097 2466 EOS011 = -1.3336301113e+01 2467 EOS111 = -4.4870114575 2468 EOS211 = 5.0042598061 2469 EOS311 = -6.5399043664e-01 2470 EOS021 = 6.7080479603 2471 EOS121 = 3.5063081279 2472 EOS221 = -1.8795372996 2473 EOS031 = -2.4649669534 2474 EOS131 = -5.5077101279e-01 2475 EOS041 = 5.5927935970e-01 2476 EOS002 = 2.0660924175 2477 EOS102 = -4.9527603989 2478 EOS202 = 2.5019633244 2479 EOS012 = 2.0564311499 2480 EOS112 = -2.1311365518e-01 2481 EOS022 = -1.2419983026 2482 EOS003 = -2.3342758797e-02 2483 EOS103 = -1.8507636718e-02 2484 EOS013 = 3.7969820455e-01 2113 2485 2114 2486 def rhop ( ptemp, psal ) : 2115 ''' 2116 Potential density referenced to surface 2487 '''Returns potential density referenced to surface 2488 2117 2489 Computation from NEMO code 2118 2490 ''' 2119 zt = ptemp * r1_T0 # Temperature (°C)2120 zs = np.sqrt ( np.abs( psal + rdeltaS ) * r1_S0 ) # Square root of salinity (PSS)2491 zt = ptemp * R1_T0 # Temperature (°C) 2492 zs = np.sqrt ( np.abs( psal + RDELTAS ) * R1_S0 ) # Square root of salinity (PSS) 2121 2493 # 2122 prhop = (((((EOS060*zt \ 2123 + EOS150*zs + EOS050)*zt \ 2124 + (EOS240*zs + EOS140)*zs + EOS040)*zt \ 2125 + ((EOS330*zs + EOS230)*zs + EOS130)*zs + EOS030)*zt \ 2126 + (((EOS420*zs + EOS320)*zs + EOS220)*zs + EOS120)*zs + EOS020)*zt \ 2127 + ((((EOS510*zs + EOS410)*zs + EOS310)*zs + EOS210)*zs + EOS110)*zs + EOS010)*zt \ 2128 + (((((EOS600*zs+ EOS500)*zs + EOS400)*zs + EOS300)*zs + EOS200)*zs + EOS100)*zs + EOS000 2494 prhop = ( 2495 (((((EOS060*zt 2496 + EOS150*zs + EOS050)*zt 2497 + (EOS240*zs + EOS140)*zs + EOS040)*zt 2498 + ((EOS330*zs + EOS230)*zs + EOS130)*zs + EOS030)*zt 2499 + (((EOS420*zs + EOS320)*zs + EOS220)*zs + EOS120)*zs + EOS020)*zt 2500 + ((((EOS510*zs + EOS410)*zs + EOS310)*zs + EOS210)*zs + EOS110)*zs + EOS010)*zt 2501 + (((((EOS600*zs+ EOS500)*zs + EOS400)*zs + EOS300)*zs + EOS200)*zs + EOS100)*zs + EOS000 ) 2129 2502 # 2130 2503 return prhop 2131 2504 2132 2505 def rho ( pdep, ptemp, psal ) : 2133 ''' 2134 In situ density 2506 '''Returns in situ density 2507 2135 2508 Computation from NEMO code 2136 2509 ''' 2137 zh = pdep * r1_Z0 # Depth (m)2138 zt = ptemp * r1_T0 # Temperature (°C)2139 zs = np.sqrt ( np.abs( psal + rdeltaS ) * r1_S0 ) # Square root salinity (PSS)2510 zh = pdep * R1_Z0 # Depth (m) 2511 zt = ptemp * R1_T0 # Temperature (°C) 2512 zs = np.sqrt ( np.abs( psal + RDELTAS ) * R1_S0 ) # Square root salinity (PSS) 2140 2513 # 2141 2514 zn3 = EOS013*zt + EOS103*zs+EOS003 … … 2143 2516 zn2 = (EOS022*zt + EOS112*zs+EOS012)*zt + (EOS202*zs+EOS102)*zs+EOS002 2144 2517 # 2145 zn1 = (((EOS041*zt \ 2146 + EOS131*zs + EOS031)*zt \ 2147 + (EOS221*zs + EOS121)*zs + EOS021)*zt \ 2148 + ((EOS311*zs + EOS211)*zs + EOS111)*zs + EOS011)*zt \ 2149 + (((EOS401*zs + EOS301)*zs + EOS201)*zs + EOS101)*zs + EOS001 2518 zn1 = ( 2519 (((EOS041*zt 2520 + EOS131*zs + EOS031)*zt 2521 + (EOS221*zs + EOS121)*zs + EOS021)*zt 2522 + ((EOS311*zs + EOS211)*zs + EOS111)*zs + EOS011)*zt 2523 + (((EOS401*zs + EOS301)*zs + EOS201)*zs + EOS101)*zs + EOS001 ) 2150 2524 # 2151 zn0 = (((((EOS060*zt \ 2152 + EOS150*zs + EOS050)*zt \ 2153 + (EOS240*zs + EOS140)*zs + EOS040)*zt \ 2154 + ((EOS330*zs + EOS230)*zs + EOS130)*zs + EOS030)*zt \ 2155 + (((EOS420*zs + EOS320)*zs + EOS220)*zs + EOS120)*zs + EOS020)*zt \ 2156 + ((((EOS510*zs + EOS410)*zs + EOS310)*zs + EOS210)*zs + EOS110)*zs + EOS010)*zt \ 2157 + (((((EOS600*zs + EOS500)*zs + EOS400)*zs + EOS300)*zs + EOS200)*zs + EOS100)*zs + EOS000 2525 zn0 = ( 2526 (((((EOS060*zt 2527 + EOS150*zs + EOS050)*zt 2528 + (EOS240*zs + EOS140)*zs + EOS040)*zt 2529 + ((EOS330*zs + EOS230)*zs + EOS130)*zs + EOS030)*zt 2530 + (((EOS420*zs + EOS320)*zs + EOS220)*zs + EOS120)*zs + EOS020)*zt 2531 + ((((EOS510*zs + EOS410)*zs + EOS310)*zs + EOS210)*zs + EOS110)*zs + EOS010)*zt 2532 + (((((EOS600*zs + EOS500)*zs + EOS400)*zs + EOS300)*zs + 2533 EOS200)*zs + EOS100)*zs + EOS000 ) 2158 2534 # 2159 2535 prho = ( ( zn3 * zh + zn2 ) * zh + zn1 ) * zh + zn0 … … 2167 2543 ## =========================================================================== 2168 2544 2169 def __is_orca_north_fold__ ( Xtest, cname_long='T' ) :2170 '''2171 Ported (pirated !!?) from Sosie2172 2173 Tell if there is a 2/point band overlaping folding at the north pole typical of the ORCA grid2174 2175 0 => not an orca grid (or unknown one)2176 4 => North fold T-point pivot (ex: ORCA2)2177 6 => North fold F-point pivot (ex: ORCA1)2178 2179 We need all this 'cname_long' stuff because with our method, there is a2180 confusion between "Grid_U with T-fold" and "Grid_V with F-fold"2181 => so knowing the name of the longitude array (as in namelist, and hence as2182 in netcdf file) might help taking the righ decision !!! UGLY!!!2183 => not implemented yet2184 '''2185 2186 ifld_nord = 0 ; cgrd_type = 'X'2187 ny, nx = Xtest.shape[-2:]2188 2189 if ny > 3 : # (case if called with a 1D array, ignoring...)2190 if ( Xtest [ny-1, 1:nx//2-1] - Xtest [ny-3, nx-1:nx-nx//2+1:-1] ).sum() == 0. :2191 ifld_nord = 4 ; cgrd_type = 'T' # T-pivot, grid_T 2192 2193 if ( Xtest [ny-1, 1:nx//2-1] - Xtest [ny-3, nx-2:nx-nx//2 :-1] ).sum() == 0. :2194 if cnlon == 'U' : ifld_nord = 4 ; cgrd_type = 'U' # T-pivot, grid_T2195 ## LOLO: PROBLEM == 6, V !!!2196 2197 if ( Xtest [ny-1, 1:nx//2-1] - Xtest [ny-3, nx-1:nx-nx//2+1:-1] ).sum() == 0. :2198 ifld_nord = 4 ; cgrd_type = 'V' # T-pivot, grid_V2199 2200 if ( Xtest [ny-1, 1:nx//2-1] - Xtest [ny-2, nx-1-1:nx-nx//2:-1] ).sum() == 0. :2201 ifld_nord = 6 ; cgrd_type = 'T'# F-pivot, grid_T2202 2203 if ( Xtest [ny-1, 1:nx//2-1] - Xtest [ny-1, nx-1:nx-nx//2-1:-1] ).sum() == 0. :2204 ifld_nord = 6 ; cgrd_type = 'U' # F-pivot, grid_U2205 2206 if ( Xtest [ny-1, 1:nx//2-1] - Xtest [ny-3, nx-2:nx-nx//2 :-1] ).sum() == 0. :2207 if cnlon == 'V' : ifld_nord = 6 ; cgrd_type = 'V' # F-pivot, grid_V2208 ## LOLO: PROBLEM == 4, U !!!2209 2210 return ifld_nord, cgrd_type2545 # def __is_orca_north_fold__ ( Xtest, cname_long='T' ) : 2546 # ''' 2547 # Ported (pirated !!?) from Sosie 2548 2549 # Tell if there is a 2/point band overlaping folding at the north pole typical of the ORCA grid 2550 2551 # 0 => not an orca grid (or unknown one) 2552 # 4 => North fold T-point pivot (ex: ORCA2) 2553 # 6 => North fold F-point pivot (ex: ORCA1) 2554 2555 # We need all this 'cname_long' stuff because with our method, there is a 2556 # confusion between "Grid_U with T-fold" and "Grid_V with F-fold" 2557 # => so knowing the name of the longitude array (as in namelist, and hence as 2558 # in netcdf file) might help taking the righ decision !!! UGLY!!! 2559 # => not implemented yet 2560 # ''' 2561 2562 # ifld_nord = 0 ; cgrd_type = 'X' 2563 # ny, nx = Xtest.shape[-2:] 2564 2565 # if ny > 3 : # (case if called with a 1D array, ignoring...) 2566 # if ( Xtest [ny-1, 1:nx//2-1] - Xtest [ny-3, nx-1:nx-nx//2+1:-1] ).sum() == 0. : 2567 # ifld_nord = 4 ; cgrd_type = 'T' # T-pivot, grid_T 2568 2569 # if ( Xtest [ny-1, 1:nx//2-1] - Xtest [ny-3, nx-2:nx-nx//2 :-1] ).sum() == 0. : 2570 # if cnlon == 'U' : ifld_nord = 4 ; cgrd_type = 'U' # T-pivot, grid_T 2571 # ## LOLO: PROBLEM == 6, V !!! 2572 2573 # if ( Xtest [ny-1, 1:nx//2-1] - Xtest [ny-3, nx-1:nx-nx//2+1:-1] ).sum() == 0. : 2574 # ifld_nord = 4 ; cgrd_type = 'V' # T-pivot, grid_V 2575 2576 # if ( Xtest [ny-1, 1:nx//2-1] - Xtest [ny-2, nx-1-1:nx-nx//2:-1] ).sum() == 0. : 2577 # ifld_nord = 6 ; cgrd_type = 'T'# F-pivot, grid_T 2578 2579 # if ( Xtest [ny-1, 1:nx//2-1] - Xtest [ny-1, nx-1:nx-nx//2-1:-1] ).sum() == 0. : 2580 # ifld_nord = 6 ; cgrd_type = 'U' # F-pivot, grid_U 2581 2582 # if ( Xtest [ny-1, 1:nx//2-1] - Xtest [ny-3, nx-2:nx-nx//2 :-1] ).sum() == 0. : 2583 # if cnlon == 'V' : ifld_nord = 6 ; cgrd_type = 'V' # F-pivot, grid_V 2584 # ## LOLO: PROBLEM == 4, U !!! 2585 2586 # return ifld_nord, cgrd_type
Note: See TracChangeset
for help on using the changeset viewer.