From e7c976dbfa5c7388fcf92ccec0ac78a81b736a10 Mon Sep 17 00:00:00 2001 From: Fei Ye Date: Thu, 18 Apr 2024 12:13:15 -0400 Subject: [PATCH] STOFS operational scripts: fixed a bug in extract_slab from a distance below the free surface. Also reorganized the code. --- .../Extract/extract_slab_fcst_netcdf4.py | 471 +++++++++--------- 1 file changed, 231 insertions(+), 240 deletions(-) diff --git a/src/Utility/Pre-Processing/STOFS-3D-Atl-operation/Extract/extract_slab_fcst_netcdf4.py b/src/Utility/Pre-Processing/STOFS-3D-Atl-operation/Extract/extract_slab_fcst_netcdf4.py index b0da32448..5ac80a9a0 100644 --- a/src/Utility/Pre-Processing/STOFS-3D-Atl-operation/Extract/extract_slab_fcst_netcdf4.py +++ b/src/Utility/Pre-Processing/STOFS-3D-Atl-operation/Extract/extract_slab_fcst_netcdf4.py @@ -1,7 +1,6 @@ -#!/usr/bin/env python3 import sys from datetime import datetime, timedelta -from time import time +from time import time import argparse from dateutil import parser @@ -10,42 +9,161 @@ from generate_adcirc import split_quads # modified by FY -def get_zcor_interp_coefficient(zcor, zinter, kbp): +def read_vgrid(vgrid_file): + time_start = time() + fid=open(vgrid_file,'r') + lines=fid.readlines() + fid.close() + ivcor=int(lines[0].strip().split()[0]) + nvrt=int(lines[1].strip().split()[0]) + lines=lines[2:] + sline=np.array(lines[0].split()).astype('float') + if sline.min()<0: + kbp=np.array([int(i.split()[1])-1 for i in lines]) + NP=len(kbp) + print(f'NP is {NP}') + sigma=-np.ones([NP,nvrt]) + for i, line in enumerate(lines): + sigma[i,kbp[i]:]=np.array(line.strip().split()[2:]).astype('float') + else: + sline=sline.astype('int') + kbp=sline-1 + NP=len(sline) + sigma=np.array([line.split()[1:] for line in lines[1:]]).T.astype('float') + fpm=sigma<-1 + sigma[fpm]=-1 + print(f"read vgrid took {time()-time_start}") + + return nvrt, sigma + +def vertical_interp(var, zcor, zinter, bottom_index): ''' inputs: + -var: var[np,nvrt] for each time record. -zcor: zcor[np,nvrt] for each time record. - -zinter: zinter[np] depth where currents will be interpolated at - -kbp - outputs: - -k1[np]: integer, k-level at each node - -coeff[np]: interpolation coefficient + -zinter[np, ]: zinter depth where currents will be interpolated at + -bottom_index[np, ]: bottom level, 0-based index ''' - #surface - idxs=zinter>=zcor[:,-1] - k1[idxs]=nvrt-2 - coeff[idxs]=1.0 - - #bottom - idxs=zinter= zcor[:,-1] + if sum(~idx) == 0: # all are surface, no need to interpolate, return the surface values + return var[:, -1] + else: # record vertical index and weight for points where zinter is above surface + target_level[idx] = nvrt-2 # i.e., surface level (nvr-1) is the upper level + lower_level_weight[idx] = 0.0 # weight of the upper level is 1.0 + + # get all points where zinter is below the bottom + idx = zinter < zcor[row_indices,bottom_index] + if sum(~idx) == 0: # all are bottom, no need to interpolate, return the bottom values + return var[row_indices, bottom_index] + else: # record vertical index and weight for points where zinter is below the bottom + target_level[idx] = bottom_index[idx] + lower_level_weight[idx] = 1.0 # weight of the lower level is 1.0 + + #intermediate for k in np.arange(nvrt-1): - idxs=(zinter>=zcor[:,k])*(zinter=zcor[:,k])*(zinter10000)]=-99999 - temp_sur[np.where(temp_sur>10000)]=-99999 - salt_sur[np.where(salt_sur>10000)]=-99999 - uvel_sur[np.where(uvel_sur>10000)]=-99999 - vvel_sur[np.where(vvel_sur>10000)]=-99999 - - temp_bot[np.where(temp_bot>10000)]=-99999 - salt_bot[np.where(salt_bot>10000)]=-99999 - uvel_bot[np.where(uvel_bot>10000)]=-99999 - vvel_bot[np.where(vvel_bot>10000)]=-99999 + time_indices, node_indices = np.ogrid[0:ntimes, 0:NP] + # initialize arrays to store extracted data + extracted_data = {} + for var_name, var_info in var_dict.items(): + print(f"Extracting {var_name}") + # nc_file = f"{fpath}/outputs/{var_info['file_prefix']}_{sid}.nc" + data = schism_output[var_info['var_name']] + + # output data is always 2D + output_data = np.zeros((ntimes, NP)) + + # vertical interpolation + if var_info['level'] == 'surface': + output_data = data[:, :, -1] + elif var_info['level'] == 'bottom': + output_data = data[time_indices, node_indices, bottom_index_node] # numpy fancy indexing + elif var_info['level'] == 'near bottom': + output_data = data[time_indices, node_indices, bottom_index_node+1] # 1 level above the bottom + else: + if type(var_info['level']) not in [int, float]: + raise ValueError('Invalid level value') + for it in np.arange(ntimes): + zinter = np.zeros(NP, dtype=float) + zinter[~idry[it, :]] = var_info['level'] + elev2d[it, ~idry[it, :]] # zinter is relative to the free surface + output_data[it, :] = vertical_interp(data[it, :, :], zcor[it, :, :], zinter[:], bottom_index_node[:]) + + # Mask dry nodes + output_data[idry]=-99999 + output_data[output_data>10000]=-99999 # mask large values, to be consistent with older version of the script + + # store extracted data + extracted_data[var_name] = output_data.copy() - uvel_inter[np.where(uvel_inter>10000)]=-99999 - vvel_inter[np.where(vvel_inter>10000)]=-99999 -# outdir= '/home1/06923/hyu05/work/oper_3D/run/extract' with Dataset(f"{outdir}/schout_UV4.5m_{sid}.nc", "w", format="NETCDF4") as fout: #dimensions fout.createDimension('time', None) fout.createDimension('nSCHISM_hgrid_node', NP) fout.createDimension('nSCHISM_hgrid_face', NE) - fout.createDimension('nMaxSCHISM_hgrid_face_nodes', NV) + fout.createDimension('nMaxSCHISM_hgrid_face_nodes', max_ele_nodes) #variables fout.createVariable('time', 'f', ('time',)) @@ -290,68 +335,14 @@ def get_zcor_interp_coefficient(zcor, zinter, kbp): #fout['elev'].missing_value=np.nan fout['elev'][:,:]=elev2d - fout.createVariable('temp_surface','f8', ('time', 'nSCHISM_hgrid_node',),fill_value=-99999) - fout['temp_surface'].long_name="sea surface temperature" - fout['temp_surface'].units="deg C" - #fout['temp'].missing_value=np.nan - fout['temp_surface'][:,:]=temp_sur - - fout.createVariable('temp_bottom','f8', ('time', 'nSCHISM_hgrid_node',),fill_value=-99999) - fout['temp_bottom'].long_name="Bottom temperature" - fout['temp_bottom'].units="deg C" - #fout['temp'].missing_value=np.nan - fout['temp_bottom'][:,:]=temp_bot - - fout.createVariable('salt_surface','f8', ('time', 'nSCHISM_hgrid_node',), fill_value=-99999) - fout['salt_surface'].long_name="sea surface salinity" - fout['salt_surface'].units="psu" - #fout['salt'].missing_value=np.nan - fout['salt_surface'][:,:]=salt_sur - - fout.createVariable('salt_bottom','f8', ('time', 'nSCHISM_hgrid_node',), fill_value=-99999) - fout['salt_bottom'].long_name="Bottom salinity" - fout['salt_bottom'].units="psu" - #fout['salt'].missing_value=np.nan - fout['salt_bottom'][:,:]=salt_bot - - fout.createVariable('uvel_surface','f8', ('time', 'nSCHISM_hgrid_node',), fill_value=-99999) - fout['uvel_surface'].long_name="U-component at the surface" - fout['uvel_surface'].units="m/s" - #fout['uvel'].missing_value=np.nan - fout['uvel_surface'][:,:]=uvel_sur - - fout.createVariable('vvel_surface','f8', ('time', 'nSCHISM_hgrid_node',), fill_value=-99999) - fout['vvel_surface'].long_name="V-component at the surface" - fout['vvel_surface'].units="m/s" - #fout['vvel'].missing_value=np.nan - fout['vvel_surface'][:,:]=vvel_sur - - fout.createVariable('uvel_bottom','f8', ('time', 'nSCHISM_hgrid_node',), fill_value=-99999) - fout['uvel_bottom'].long_name="U-component at the bottom" - fout['uvel_bottom'].units="m/s" - #fout['uvel'].missing_value=np.nan - fout['uvel_bottom'][:,:]=uvel_bot - - fout.createVariable('vvel_bottom','f8', ('time', 'nSCHISM_hgrid_node',), fill_value=-99999) - fout['vvel_bottom'].long_name="V-component at the bottom" - fout['vvel_bottom'].units="m/s" - #fout['vvel'].missing_value=np.nan - fout['vvel_bottom'][:,:]=vvel_bot - - fout.createVariable('uvel4.5','f8', ('time', 'nSCHISM_hgrid_node',), fill_value=-99999) - fout['uvel4.5'].long_name="U-component at 4.5m below free surface" - fout['uvel4.5'].units="m/s" - #fout['uvel'].missing_value=np.nan - fout['uvel4.5'][:,:]=uvel_inter - - fout.createVariable('vvel4.5','f8', ('time', 'nSCHISM_hgrid_node',), fill_value=-99999) - fout['vvel4.5'].long_name="V-component at 4.5m below free surface" - fout['vvel4.5'].units="m/s" - #fout['vvel'].missing_value=np.nan - fout['vvel4.5'][:,:]=vvel_inter + for var_name, var_info in var_dict.items(): + fout.createVariable(var_name, 'f8', ('time', 'nSCHISM_hgrid_node',), fill_value=-99999) + fout[var_name].long_name=var_info['long name'] + fout[var_name].units=var_info['units'] + fout[var_name][:, :] = extracted_data[var_name] fout.title = 'SCHISM Model output' fout.source = 'SCHISM model output version v10' fout.references = 'http://ccrm.vims.edu/schismweb/' - print(f'It took {time()-t0} to interpolate') + print(f'Extraction took {time()-t0} seconds')