def __interp3_vel_thread(rx, ry, rz, ra, u, v, zx, zy, zz, za, pmap, weight, nx, ny, mask): """ internal routine: 3D velocity interpolation thread for parallel interpolation """ # Put on the same grid if u.shape != v.shape: u = seapy.model.u2rho(u, fill=True) v = seapy.model.v2rho(v, fill=True) # Rotate the fields (NOTE: ROMS angle is negative relative to "true") if ra is not None: u, v = seapy.rotate(u, v, ra) # Interpolate u = __interp3_thread(rx, ry, rz, u, zx, zy, zz, pmap, weight, nx, ny, mask, _up_scaling["u"], _down_scaling["u"]) v = __interp3_thread(rx, ry, rz, v, zx, zy, zz, pmap, weight, nx, ny, mask, _up_scaling["v"], _down_scaling["v"]) # Rotate to destination (NOTE: ROMS angle is negative relative to "true") if za is not None: u, v = seapy.rotate(u, v, -za) # Return the masked data return u, v
def detide(grid, bryfile, tidefile, tides=None, tide_start=None): """ Given a boundary file, detide the barotropic components and create tidal forcing file for the grid. This method will update the given boundary file. Parameters ---------- grid : seapy.model.grid or string, The grid that defines the boundaries shape and mask bryfile : string, The boundary file to detide tidefile : string, The output tidal forcing file with the tide spectral forcing tides : string array, optional Array of strings defining which tides to extract. Defaults to the standard 11 constituents. tide_start : datetime, optional The reference date to use for the tide forcing. If None, the center of the time period is used. Returns ------- None Examples -------- Make a long time-series boundary conditions from a group of boundary files, skipping the last record of each file to prevent overlap (if there are 100 records in each file). Afterwards, detide the resulting file. >>> !ncrcat -dbry_time,0,,100,99 bry_out_*nc bry_detide.nc >>> seapy.roms.boundary.detide("mygrid.nc", "bry_detide.nc", "tide_out.nc") """ import datetime if not tides: tides = seapy.tide.default_tides else: tides = np.atleast_1d(tides) # Load Files grid = seapy.model.asgrid(grid) bry = netCDF4.Dataset(bryfile, "a") # Get the definitions of the boundary file epoch, timevar = seapy.roms.get_reftime(bry) time = seapy.roms.num2date(bry, timevar) # Pick the time for the tide file reference if not tide_start: tide_start = time[0] + (time[-1] - time[0]) / 2 tide_start = datetime.datetime( tide_start.year, tide_start.month, tide_start.day) try: s_rho = len(bry.dimensions['s_rho']) except: s_rho = grid.n # Set variables to detide detide_vars = ['zeta', 'ubar', 'vbar'] # Create the tide forcing file bry.detide = "Detided to generate tide forcing: {:s}".format(tidefile) # Detide the free-surface eamp = np.zeros((len(tides), grid.eta_rho, grid.xi_rho)) epha = np.zeros((len(tides), grid.eta_rho, grid.xi_rho)) cmin = np.zeros((len(tides), grid.eta_rho, grid.xi_rho)) cmax = np.zeros((len(tides), grid.eta_rho, grid.xi_rho)) cang = np.zeros((len(tides), grid.eta_rho, grid.xi_rho)) cpha = np.zeros((len(tides), grid.eta_rho, grid.xi_rho)) for side in sides: lvar = "zeta_" + side idx = sides[side].indices lat = grid.lat_rho[idx[0], idx[1]] size = grid.xi_rho if sides[side].xi else grid.eta_rho if lvar in bry.variables: print(lvar) zeta = np.ma.array(bry.variables[lvar][:]) mask = np.ma.getmaskarray(zeta) # Detide for i in seapy.progressbar.progress(range(size)): if np.any(mask[:, i]): continue out = seapy.tide.fit(time, zeta[:, i], tides=tides, lat=lat[i], tide_start=tide_start) zeta[:, i] -= out['fit'].data # Save the amp/phase in the tide file for n, t in enumerate(tides): if sides[side].xi: eamp[n, idx[0], i] = out['major'][t].amp epha[n, idx[0], i] = np.mod( out['major'][t].phase, 2 * np.pi) else: eamp[n, i, idx[1]] = out['major'][t].amp epha[n, i, idx[1]] = np.mod( out['major'][t].phase, 2 * np.pi) # Save out the detided information bry.variables[lvar][:] = zeta zeta = [0] bry.sync() # Detide the barotropic velocity uvar = "ubar_" + side vvar = "vbar_" + side if uvar in bry.variables and vvar in bry.variables: print(uvar, vvar) ubar = np.zeros((len(time), size)) vbar = np.zeros((len(time), size)) # Load data, put onto rho-grid, and rotate bubar = np.ma.array(bry.variables[uvar][:]).filled(0) bvbar = np.ma.array(bry.variables[vvar][:]).filled(0) if sides[side].xi: ubar[:, 1:-1] = 0.5 * (bubar[:, 1:] + bubar[:, :-1]) ubar[:, 0] = bubar[:, 1] ubar[:, -1] = bubar[:, -2] vbar = bvbar.copy() else: vbar[:, 1:-1] = 0.5 * (bvbar[:, 1:] + bvbar[:, :-1]) vbar[:, 0] = bvbar[:, 1] vbar[:, -1] = bvbar[:, -2] ubar = bubar.copy() ubar, vbar = seapy.rotate(ubar, vbar, grid.angle[idx[0], idx[1]]) bubar = bvbar = [] # Detide for i in seapy.progressbar.progress(range(size)): if np.any(mask[:, i]): continue out = seapy.tide.fit( time, ubar[:, i] + 1j * vbar[:, i], tides=tides, lat=lat[i], tide_start=tide_start) ubar[:, i] -= np.real(out['fit']) vbar[:, i] -= np.imag(out['fit']) # Save the amp/phase in the tide file for n, t in enumerate(tides): if sides[side].xi: cmax[n, idx[0], i] = out['major'][t].amp cmin[n, idx[0], i] = out['minor'][t].amp cpha[n, idx[0], i] = out['major'][t].phase cang[n, idx[0], i] = out['minor'][t].phase else: cmax[n, i, idx[1]] = out['major'][t].amp cmin[n, i, idx[1]] = out['minor'][t].amp cpha[n, i, idx[1]] = out['major'][t].phase cang[n, i, idx[1]] = out['minor'][t].phase ubar, vbar = seapy.rotate(ubar, vbar, -grid.angle[idx[0], idx[1]]) if sides[side].xi: bry.variables[uvar][:] = 0.5 * (ubar[:, 1:] + ubar[:, :-1]) bry.variables[vvar][:] = vbar else: bry.variables[vvar][:] = 0.5 * (vbar[:, 1:] + vbar[:, :-1]) bry.variables[uvar][:] = ubar bry.sync() ubar = vbar = [] # Have to duplicate the boundary tide info into the inner row/column eamp[:, 1:-1, 1] = eamp[:, 1:-1, 0] eamp[:, 1:-1, -2] = eamp[:, 1:-1, -1] eamp[:, 1, 1:-1] = eamp[:, 0, 1:-1] eamp[:, -2, 1:-1] = eamp[:, -1, 1:-1] epha[:, 1:-1, 1] = epha[:, 1:-1, 0] epha[:, 1:-1, -2] = epha[:, 1:-1, -1] epha[:, 1, 1:-1] = epha[:, 0, 1:-1] epha[:, -2, 1:-1] = epha[:, -1, 1:-1] cmax[:, 1:-1, 1] = cmax[:, 1:-1, 0] cmax[:, 1:-1, -2] = cmax[:, 1:-1, -1] cmax[:, 1, 1:-1] = cmax[:, 0, 1:-1] cmax[:, -2, 1:-1] = cmax[:, -1, 1:-1] cmin[:, 1:-1, 1] = cmin[:, 1:-1, 0] cmin[:, 1:-1, -2] = cmin[:, 1:-1, -1] cmin[:, 1, 1:-1] = cmin[:, 0, 1:-1] cmin[:, -2, 1:-1] = cmin[:, -1, 1:-1] cpha[:, 1:-1, 1] = cpha[:, 1:-1, 0] cpha[:, 1:-1, -2] = cpha[:, 1:-1, -1] cpha[:, 1, 1:-1] = cpha[:, 0, 1:-1] cpha[:, -2, 1:-1] = cpha[:, -1, 1:-1] cang[:, 1:-1, 1] = cang[:, 1:-1, 0] cang[:, 1:-1, -2] = cang[:, 1:-1, -1] cang[:, 1, 1:-1] = cang[:, 0, 1:-1] cang[:, -2, 1:-1] = cang[:, -1, 1:-1] # Set the tide reference tideout = {} tideout['tides'] = tides tideout['tide_start'] = tide_start tideout['Eamp'] = eamp tideout['Ephase'] = epha tideout['Cmajor'] = cmax tideout['Cminor'] = cmin tideout['Cphase'] = cpha tideout['Cangle'] = cang seapy.roms.tide.create_forcing(tidefile, tideout, title="Tides from " + bryfile, epoch=epoch) bry.close() pass
def from_stations(station_file, bry_file, grid=None): """ Construct a boundary forcing file from a stations file generated by a parent-grid. The stations.in file must have been generated by the seapy.roms.gen_stations method; otherwise, the order will be incorrect. Parameters ========== station_file : string Filename of the stations file that is the source for the boundary data bry_file : string Filename of the boundary conditions file to generate grid : string or seapy.model.grid Grid that the boundary conditions are created for Returns ------- None """ grid = seapy.model.asgrid(grid) ncstation = netCDF4.Dataset(station_file) src_ref, time = seapy.roms.get_reftime(ncstation) # Create the boundary file and fill up the descriptive data ncbry = seapy.roms.ncgen.create_bry(bry_file, eta_rho=grid.eta_rho, xi_rho=grid.xi_rho, s_rho=grid.n, reftime=src_ref, clobber=False, title="generated from " + station_file) grid.to_netcdf(ncbry) # Load the times: we need to see if the times are duplicated # because if using assimilation, they may be duplicated for every # outer-loop. Currently, it overwrites the first one each time (this # will need to be fixed if ROMS is fixed). statime = ncstation.variables[time][:] dup = np.where(statime[1:] == statime[0])[0] rng = np.s_[:] if dup.size > 0: rng = np.s_[0:np.min(dup)] statime = statime[rng] brytime = seapy.roms.get_timevar(ncbry) ncbry.variables[brytime][:] = seapy.roms.date2num( seapy.roms.num2date(ncstation, time, rng), ncbry, brytime) # Set up the indices bry = { "south": range(0, grid.lm), "north": range(grid.lm, 2 * grid.lm), "west": range(2 * grid.lm, 2 * grid.lm + grid.ln), "east": range(2 * grid.lm + grid.ln, 2 * (grid.lm + grid.ln)) } # Get the information to construct the depths of the station data sta_vt = ncstation.variables["Vtransform"][:] sta_hc = ncstation.variables["hc"][:] sta_s_rho = ncstation.variables["s_rho"][:] sta_cs_r = ncstation.variables["Cs_r"][:] sta_h = ncstation.variables["h"][:] sta_angle = ncstation.variables["angle"][:] sta_lon = ncstation.variables["lon_rho"][:] sta_lat = ncstation.variables["lat_rho"][:] sta_mask = np.ones(sta_lat.shape) sta_mask[sta_lon * sta_lat > 1e10] = 0 # Load the station data as we need to manipulate it sta_zeta = np.ma.masked_greater(ncstation.variables["zeta"][rng], 100) sta_ubar = np.ma.masked_greater(ncstation.variables["ubar"][rng], 100) sta_vbar = np.ma.masked_greater(ncstation.variables["vbar"][rng], 100) sta_temp = np.ma.masked_greater(ncstation.variables["temp"][rng], 100) sta_salt = np.ma.masked_greater(ncstation.variables["salt"][rng], 100) sta_u = np.ma.masked_greater(ncstation.variables["u"][rng], 100) sta_v = np.ma.masked_greater(ncstation.variables["v"][rng], 100) ncstation.close() # Create the true positions and mask grid_h = np.concatenate([grid.h[0, :], grid.h[-1, :], grid.h[:, 0], grid.h[:, -1]]) grid_lon = np.concatenate([grid.lon_rho[0, :], grid.lon_rho[-1, :], grid.lon_rho[:, 0], grid.lon_rho[:, -1]]) grid_lat = np.concatenate([grid.lat_rho[0, :], grid.lat_rho[-1, :], grid.lat_rho[:, 0], grid.lat_rho[:, -1]]) grid_mask = np.concatenate([grid.mask_rho[0, :], grid.mask_rho[-1, :], grid.mask_rho[:, 0], grid.mask_rho[:, -1]]) grid_angle = np.concatenate([grid.angle[0, :], grid.angle[-1, :], grid.angle[:, 0], grid.angle[:, -1]]) # Search for bad stations due to child grid overlaying parent mask. # Unfortunately, ROMS will give points that are not at the locations # you specify if those points conflict with the mask. So, these points # are simply replaced with the nearest. dist = np.sqrt((sta_lon - grid_lon)**2 + (sta_lat - grid_lat)**2) bad_pts = np.where(np.logical_and(dist > 0.001, grid_mask == 1))[0] good_pts = np.where(np.logical_and(dist < 0.001, grid_mask == 1))[0] for i in bad_pts: didx = np.sqrt((sta_lon[i] - sta_lon[good_pts])**2 + (sta_lat[i] - sta_lat[good_pts])**2).argmin() index = good_pts[didx] sta_h[i] = sta_h[index] sta_angle[i] = sta_angle[index] sta_lon[i] = sta_lon[index] sta_lat[i] = sta_lat[index] sta_zeta[:, i] = sta_zeta[:, index] sta_ubar[:, i] = sta_ubar[:, index] sta_vbar[:, i] = sta_vbar[:, index] sta_temp[:, i, :] = sta_temp[:, index, :] sta_salt[:, i, :] = sta_salt[:, index, :] sta_u[:, i, :] = sta_u[:, index, :] sta_v[:, i, :] = sta_v[:, index, :] # Construct the boundaries: a dictionary of boundary side and two element # array whether the u[0] or v[1] dimensions need to be averaged sides = {"north": [True, False], "south": [True, False], "east": [False, True], "west": [False, True]} delta_angle = sta_angle - grid_angle sta_ubar, sta_vbar = seapy.rotate(sta_ubar, sta_vbar, delta_angle) sta_u, sta_v = seapy.rotate(sta_u, sta_v, np.tile(delta_angle, (sta_u.shape[-1], 1)).T) # Set up the parameters for depth-interpolated wght = 5 nx = 3 ny = 9 # Build a non-extrapolating field to interpolate. Generate the # position and depth def __expand_field(x): shp = x.shape y = np.zeros((shp[0] + 2, shp[1] + 2)) y[1:-1, 1:-1] = x y[1:-1, 0] = x[:, 0] y[1:-1, -1] = x[:, -1] y[0, :] = y[1, :] y[-1, :] = y[-2, :] return y for side in sides: print(side) # Masks sta_ocean = np.where(sta_mask[bry[side]] == 1)[0] ocean = np.where(grid_mask[bry[side]] == 1)[0] # If we have a masked boundary, skip it if not np.any(ocean): continue # 1) Zeta ncbry.variables["zeta_" + side][:, ocean] = sta_zeta[:, bry[side]][:, ocean] # 2) Ubar if sides[side][0]: ncbry.variables["ubar_" + side][:] = 0.5 * ( sta_ubar[:, bry[side][0:-1]] + sta_ubar[:, bry[side][1:]]) else: ncbry.variables["ubar_" + side][:] = sta_ubar[:, bry[side]] # 3) Vbar if sides[side][1]: ncbry.variables["vbar_" + side][:] = 0.5 * ( sta_vbar[:, bry[side][0:-1]] + sta_vbar[:, bry[side][1:]]) else: ncbry.variables["vbar_" + side][:] = sta_vbar[:, bry[side]] # For 3D variables, we need to loop through time and interpolate # onto the child grid. Construct the distances x = np.zeros(len(bry[side])) x[1:] = np.cumsum(seapy.earth_distance(grid_lon[bry[side][0:-1]], grid_lat[bry[side][0:-1]], grid_lon[bry[side][1:]], grid_lat[bry[side][1:]])) sta_x = seapy.adddim(x, len(sta_s_rho)) x = seapy.adddim(x, len(grid.s_rho)) for n, t in seapy.progressbar.progress(enumerate(statime), statime.size): sta_depth = seapy.roms.depth(sta_vt, sta_h[bry[side]], sta_hc, sta_s_rho, sta_cs_r, sta_zeta[n, bry[side]]) depth = seapy.roms.depth(grid.vtransform, grid_h[bry[side]], grid.hc, grid.s_rho, grid.cs_r, sta_zeta[n, bry[side]]) in_x = __expand_field(sta_x[:, sta_ocean]) in_x[:, 0] = in_x[:, 0] - 3600 in_x[:, -1] = in_x[:, -1] + 3600 in_depth = __expand_field(sta_depth[:, sta_ocean]) in_depth[0, :] = in_depth[0, :] - 1000 in_depth[-1, :] = in_depth[-1, :] + 10 # 4) Temp in_data = __expand_field(np.transpose( sta_temp[n, bry[side], :][sta_ocean, :])) ncbry.variables["temp_" + side][n, :] = 0.0 ncbry.variables["temp_" + side][n, :, ocean], pmap = seapy.oa.oasurf( in_x, in_depth, in_data, x[:, ocean], depth[:, ocean], nx=nx, ny=ny, weight=wght) # 5) Salt in_data = __expand_field(np.transpose( sta_salt[n, bry[side], :][sta_ocean, :])) ncbry.variables["salt_" + side][n, :] = 0.0 ncbry.variables["salt_" + side][n, :, ocean], pmap = seapy.oa.oasurf( in_x, in_depth, in_data, x[:, ocean], depth[:, ocean], pmap=pmap, nx=nx, ny=ny, weight=wght) # 6) U in_data = __expand_field(np.transpose( sta_u[n, bry[side], :][sta_ocean, :])) data = np.zeros(x.shape) data[:, ocean], pmap = seapy.oa.oasurf(in_x, in_depth, in_data, x[:, ocean], depth[:, ocean], pmap=pmap, nx=nx, ny=ny, weight=wght) if sides[side][0]: ncbry.variables["u_" + side][n, :] = 0.5 * ( data[:, 0:-1] + data[:, 1:]) else: ncbry.variables["u_" + side][n, :] = data # 7) V in_data = __expand_field(np.transpose( sta_v[n, bry[side], :][sta_ocean, :])) data = data * 0 data[:, ocean], pmap = seapy.oa.oasurf(in_x, in_depth, in_data, x[:, ocean], depth[:, ocean], pmap=pmap, nx=nx, ny=ny, weight=wght) if sides[side][1]: ncbry.variables["v_" + side][n, :] = 0.5 * ( data[:, 0:-1] + data[:, 1:]) else: ncbry.variables["v_" + side][n, :] = data ncbry.sync() ncbry.close() pass
def mean_plot_2Dvec(u, v, g="hiomsag-grid.nc"): grid = sp.model.grid(g) m = 1 / grid.pm n = 1 / grid.pn T = len(u) x1 = np.array([ 180, 179, 178, 177, 176, 175, 174, 173, 172, 171, 170, 169, 168, 167, 166, 165, 164, 163, 162, 161, 160, 159, 158, 157, 156, 155, 154, 153, 152, 151, 150, 149, 148, 147, 146, 145, 144, 143, 142, 141, 140, 139, 138, 137, 136, 135 ]) y1 = np.array([[68, 69], [68, 69], [68, 69], [68, 69], [68, 69], [68, 69], [68, 69], [68, 69], [68, 69], [68, 69], [68, 69], [68, 69], [68, 69], [68, 69], [68, 69], [68, 69], [68, 69], [68, 69], [68, 69], [68, 69], [68, 69], [68, 69], [68, 69], [68, 69], [68, 69], [68, 69], [68, 69], [68, 69], [68, 69], [68, 69], [68, 69], [68, 69], [68, 69], [68, 69], [68, 69], [68, 69], [68, 69], [68, 69], [68, 69], [68, 69], [68, 69], [68, 69], [68, 69], [68, 69], [68, 69], [69]]) x2 = np.array([[135, 134, 133], [132, 133, 134], [131, 132, 133], [131, 132, 133], [130, 131, 132], [129, 130, 131], [129, 130, 131], [128, 129, 130], [128, 129, 130], [127, 128, 129], [127, 128, 129], [126, 127]]) y2 = np.array([67, 66, 65, 64, 63, 62, 61, 60, 59, 58, 57, 56]) x3 = np.array([[125, 126, 127], [125, 126, 127], [125, 126, 127], [125, 126, 127], [125, 126, 127], [125, 126, 127], [125, 126, 127], [125, 126, 127], [125, 126, 127], [125, 126, 127]]) y3 = np.array([54, 53, 52, 51, 50, 49, 48, 47, 46, 45]) u1 = np.zeros((T, len(x1))) v1 = np.zeros((T, len(x1))) dist1 = np.zeros(len(x1)) for i in range(0, len(x1)): u1[:, i] = np.nanmean(u[:, y1[i], x1[i] - 1], axis=-1) v1[:, i] = np.nanmean(v[:, np.asarray(y1[i]) - 1, x1[i]], axis=-1) dist1[i] = np.nanmean(m[y1[i], x1[i]]) u2 = np.zeros((T, len(x2))) v2 = np.zeros((T, len(x2))) dist2 = np.zeros(len(x2)) for i in range(0, len(x2)): #u2[:, :, i] = np.sqrt((np.mean(u[:, :, y2[i], x2[i]], axis=-1))**2 + (np.mean(v[:, :, y2[i], x2[i]], axis=-1))**2) * np.sign(np.mean(v[:, :, y2[i], x2[i]], axis=-1)) u2[:, i], v2[:, i] = sp.rotate(np.nanmean(u[:, y2[i], np.asarray(x2[i]) - 1], axis=-1), np.nanmean(v[:, y2[i] - 1, x2[i]], axis=-1), angle=np.pi / 3.0) dist2[i] = np.nanmean(m[y2[i], x2[i]]) u3 = np.zeros((T, len(x3))) v3 = np.zeros((T, len(x3))) dist3 = np.zeros(len(x3)) for i in range(0, len(x3)): u3[:, i] = np.nanmean(v[:, y3[i], np.asarray(x3[i]) - 1], axis=-1) v3[:, i] = np.nanmean(u[:, y3[i] - 1, x3[i]], axis=-1) dist3[i] = np.nanmean(m[y3[i], x3[i]]) N = len(x1) + len(x2) + len(x3) U = np.zeros((T, N)) V = np.zeros((T, N)) dist = np.zeros(N) U[:, 0:len(x1)] = u1 U[:, len(x1):len(x1) + len(x2)] = v2 U[:, len(x1) + len(x2):N] = u3 V[:, 0:len(x1)] = v1 V[:, len(x1):len(x1) + len(x2)] = -u2 V[:, len(x1) + len(x2):N] = v3 dist[0:len(x1)] = dist1 dist[len(x1):len(x1) + len(x2)] = dist2 dist[len(x1) + len(x2):N] = dist3 # calculate total distance along the canal total_dist = np.zeros(N) for i in range(0, N): total_dist[i] = np.sum(dist[0:i]) # making distance vector to 2d array for plotting purposes distance_array = np.zeros((N)) for i in range(0, N): distance_array[N - 1 - i] = total_dist[i] return U, V, distance_array
def detide(grid, bryfile, tidefile, tides=None, tide_start=None): """ Given a boundary file, detide the barotropic components and create tidal forcing file for the grid. This method will update the given boundary file. Parameters ---------- grid : seapy.model.grid or string, The grid that defines the boundaries shape and mask bryfile : string, The boundary file to detide tidefile : string, The output tidal forcing file with the tide spectral forcing tides : string array, optional Array of strings defining which tides to extract. Defaults to the standard 11 constituents. tide_start : datetime, optional The reference date to use for the tide forcing. If None, the center of the time period is used. Returns ------- None Examples -------- Make a long time-series boundary conditions from a group of boundary files, skipping the last record of each file to prevent overlap (if there are 100 records in each file). Afterwards, detide the resulting file. >>> !ncrcat -dbry_time,0,,100,99 bry_out_*nc bry_detide.nc >>> seapy.roms.boundary.detide("mygrid.nc", "bry_detide.nc", "tide_out.nc") """ import datetime if not tides: tides = seapy.tide.default_tides else: tides = np.atleast_1d(tides) # Load Files grid = seapy.model.asgrid(grid) bry = netCDF4.Dataset(bryfile, "a") # Get the definitions of the boundary file epoch, timevar = seapy.roms.get_reftime(bry) time = seapy.roms.num2date(bry, timevar) # Pick the time for the tide file reference if not tide_start: tide_start = time[0] + (time[-1] - time[0]) / 2 tide_start = datetime.datetime(tide_start.year, tide_start.month, tide_start.day) try: s_rho = len(bry.dimensions['s_rho']) except: s_rho = grid.n # Set variables to detide detide_vars = ['zeta', 'ubar', 'vbar'] # Create the tide forcing file bry.detide = "Detided to generate tide forcing: {:s}".format(tidefile) # Detide the free-surface eamp = np.zeros((len(tides), grid.eta_rho, grid.xi_rho)) epha = np.zeros((len(tides), grid.eta_rho, grid.xi_rho)) cmin = np.zeros((len(tides), grid.eta_rho, grid.xi_rho)) cmax = np.zeros((len(tides), grid.eta_rho, grid.xi_rho)) cang = np.zeros((len(tides), grid.eta_rho, grid.xi_rho)) cpha = np.zeros((len(tides), grid.eta_rho, grid.xi_rho)) for side in sides: lvar = "zeta_" + side idx = sides[side].indices lat = grid.lat_rho[idx[0], idx[1]] size = grid.xi_rho if sides[side].xi else grid.eta_rho if lvar in bry.variables: print(lvar) zeta = np.ma.array(bry.variables[lvar][:]) mask = np.ma.getmaskarray(zeta) # Detide for i in seapy.progressbar.progress(range(size)): if np.any(mask[:, i]): continue out = seapy.tide.fit(time, zeta[:, i], tides=tides, lat=lat[i], tide_start=tide_start) zeta[:, i] -= out['fit'].data # Save the amp/phase in the tide file for n, t in enumerate(tides): if sides[side].xi: eamp[n, idx[0], i] = out['major'][t].amp epha[n, idx[0], i] = np.mod(out['major'][t].phase, 2 * np.pi) else: eamp[n, i, idx[1]] = out['major'][t].amp epha[n, i, idx[1]] = np.mod(out['major'][t].phase, 2 * np.pi) # Save out the detided information bry.variables[lvar][:] = zeta zeta = [0] bry.sync() # Detide the barotropic velocity uvar = "ubar_" + side vvar = "vbar_" + side if uvar in bry.variables and vvar in bry.variables: print(uvar, vvar) ubar = np.zeros((len(time), size)) vbar = np.zeros((len(time), size)) # Load data, put onto rho-grid, and rotate bubar = np.ma.array(bry.variables[uvar][:]).filled(0) bvbar = np.ma.array(bry.variables[vvar][:]).filled(0) if sides[side].xi: ubar[:, 1:-1] = 0.5 * (bubar[:, 1:] + bubar[:, :-1]) ubar[:, 0] = bubar[:, 1] ubar[:, -1] = bubar[:, -2] vbar = bvbar.copy() else: vbar[:, 1:-1] = 0.5 * (bvbar[:, 1:] + bvbar[:, :-1]) vbar[:, 0] = bvbar[:, 1] vbar[:, -1] = bvbar[:, -2] ubar = bubar.copy() ubar, vbar = seapy.rotate(ubar, vbar, grid.angle[idx[0], idx[1]]) bubar = bvbar = [] # Detide for i in seapy.progressbar.progress(range(size)): if np.any(mask[:, i]): continue out = seapy.tide.fit(time, ubar[:, i] + 1j * vbar[:, i], tides=tides, lat=lat[i], tide_start=tide_start) ubar[:, i] -= np.real(out['fit']) vbar[:, i] -= np.imag(out['fit']) # Save the amp/phase in the tide file for n, t in enumerate(tides): if sides[side].xi: cmax[n, idx[0], i] = out['major'][t].amp cmin[n, idx[0], i] = out['minor'][t].amp cpha[n, idx[0], i] = out['major'][t].phase cang[n, idx[0], i] = out['minor'][t].phase else: cmax[n, i, idx[1]] = out['major'][t].amp cmin[n, i, idx[1]] = out['minor'][t].amp cpha[n, i, idx[1]] = out['major'][t].phase cang[n, i, idx[1]] = out['minor'][t].phase ubar, vbar = seapy.rotate(ubar, vbar, -grid.angle[idx[0], idx[1]]) if sides[side].xi: bry.variables[uvar][:] = 0.5 * (ubar[:, 1:] + ubar[:, :-1]) bry.variables[vvar][:] = vbar else: bry.variables[vvar][:] = 0.5 * (vbar[:, 1:] + vbar[:, :-1]) bry.variables[uvar][:] = ubar bry.sync() ubar = vbar = [] # Have to duplicate the boundary tide info into the inner row/column eamp[:, 1:-1, 1] = eamp[:, 1:-1, 0] eamp[:, 1:-1, -2] = eamp[:, 1:-1, -1] eamp[:, 1, 1:-1] = eamp[:, 0, 1:-1] eamp[:, -2, 1:-1] = eamp[:, -1, 1:-1] epha[:, 1:-1, 1] = epha[:, 1:-1, 0] epha[:, 1:-1, -2] = epha[:, 1:-1, -1] epha[:, 1, 1:-1] = epha[:, 0, 1:-1] epha[:, -2, 1:-1] = epha[:, -1, 1:-1] cmax[:, 1:-1, 1] = cmax[:, 1:-1, 0] cmax[:, 1:-1, -2] = cmax[:, 1:-1, -1] cmax[:, 1, 1:-1] = cmax[:, 0, 1:-1] cmax[:, -2, 1:-1] = cmax[:, -1, 1:-1] cmin[:, 1:-1, 1] = cmin[:, 1:-1, 0] cmin[:, 1:-1, -2] = cmin[:, 1:-1, -1] cmin[:, 1, 1:-1] = cmin[:, 0, 1:-1] cmin[:, -2, 1:-1] = cmin[:, -1, 1:-1] cpha[:, 1:-1, 1] = cpha[:, 1:-1, 0] cpha[:, 1:-1, -2] = cpha[:, 1:-1, -1] cpha[:, 1, 1:-1] = cpha[:, 0, 1:-1] cpha[:, -2, 1:-1] = cpha[:, -1, 1:-1] cang[:, 1:-1, 1] = cang[:, 1:-1, 0] cang[:, 1:-1, -2] = cang[:, 1:-1, -1] cang[:, 1, 1:-1] = cang[:, 0, 1:-1] cang[:, -2, 1:-1] = cang[:, -1, 1:-1] # Set the tide reference tideout = {} tideout['tides'] = tides tideout['tide_start'] = tide_start tideout['Eamp'] = eamp tideout['Ephase'] = epha tideout['Cmajor'] = cmax tideout['Cminor'] = cmin tideout['Cphase'] = cpha tideout['Cangle'] = cang seapy.roms.tide.create_forcing(tidefile, tideout, title="Tides from " + bryfile, epoch=epoch) bry.close() pass
def from_stations(station_file, bry_file, grid=None): """ Construct a boundary forcing file from a stations file generated by a parent-grid. The stations.in file must have been generated by the seapy.roms.gen_stations method; otherwise, the order will be incorrect. Parameters ========== station_file : string Filename of the stations file that is the source for the boundary data bry_file : string Filename of the boundary conditions file to generate grid : string or seapy.model.grid Grid that the boundary conditions are created for Returns ------- None """ grid = seapy.model.asgrid(grid) ncstation = netCDF4.Dataset(station_file) src_ref, time = seapy.roms.get_reftime(ncstation) # Create the boundary file and fill up the descriptive data ncbry = seapy.roms.ncgen.create_bry(bry_file, eta_rho=grid.eta_rho, xi_rho=grid.xi_rho, s_rho=grid.n, reftime=src_ref, clobber=False, title="generated from " + station_file) grid.to_netcdf(ncbry) # Load the times: we need to see if the times are duplicated # because if using assimilation, they may be duplicated for every # outer-loop. Currently, it overwrites the first one each time (this # will need to be fixed if ROMS is fixed). statime = ncstation.variables[time][:] dup = np.where(statime[1:] == statime[0])[0] rng = np.s_[:] if dup.size > 0: rng = np.s_[0:np.min(dup)] statime = statime[rng] brytime = seapy.roms.get_timevar(ncbry) ncbry.variables[brytime][:] = seapy.roms.date2num( seapy.roms.num2date(ncstation, time, rng), ncbry, brytime) # Set up the indices bry = { "south": range(0, grid.lm), "north": range(grid.lm, 2 * grid.lm), "west": range(2 * grid.lm, 2 * grid.lm + grid.ln), "east": range(2 * grid.lm + grid.ln, 2 * (grid.lm + grid.ln)) } # Get the information to construct the depths of the station data sta_vt = ncstation.variables["Vtransform"][:] sta_hc = ncstation.variables["hc"][:] sta_s_rho = ncstation.variables["s_rho"][:] sta_cs_r = ncstation.variables["Cs_r"][:] sta_h = ncstation.variables["h"][:] sta_angle = ncstation.variables["angle"][:] sta_lon = ncstation.variables["lon_rho"][:] sta_lat = ncstation.variables["lat_rho"][:] sta_mask = np.ones(sta_lat.shape) sta_mask[sta_lon * sta_lat > 1e10] = 0 # Load the station data as we need to manipulate it sta_zeta = np.ma.masked_greater(ncstation.variables["zeta"][rng], 100) sta_ubar = np.ma.masked_greater(ncstation.variables["ubar"][rng], 100) sta_vbar = np.ma.masked_greater(ncstation.variables["vbar"][rng], 100) sta_temp = np.ma.masked_greater(ncstation.variables["temp"][rng], 100) sta_salt = np.ma.masked_greater(ncstation.variables["salt"][rng], 100) sta_u = np.ma.masked_greater(ncstation.variables["u"][rng], 100) sta_v = np.ma.masked_greater(ncstation.variables["v"][rng], 100) ncstation.close() # Create the true positions and mask grid_h = np.concatenate( [grid.h[0, :], grid.h[-1, :], grid.h[:, 0], grid.h[:, -1]]) grid_lon = np.concatenate([ grid.lon_rho[0, :], grid.lon_rho[-1, :], grid.lon_rho[:, 0], grid.lon_rho[:, -1] ]) grid_lat = np.concatenate([ grid.lat_rho[0, :], grid.lat_rho[-1, :], grid.lat_rho[:, 0], grid.lat_rho[:, -1] ]) grid_mask = np.concatenate([ grid.mask_rho[0, :], grid.mask_rho[-1, :], grid.mask_rho[:, 0], grid.mask_rho[:, -1] ]) grid_angle = np.concatenate([ grid.angle[0, :], grid.angle[-1, :], grid.angle[:, 0], grid.angle[:, -1] ]) # Search for bad stations due to child grid overlaying parent mask. # Unfortunately, ROMS will give points that are not at the locations # you specify if those points conflict with the mask. So, these points # are simply replaced with the nearest. dist = np.sqrt((sta_lon - grid_lon)**2 + (sta_lat - grid_lat)**2) bad_pts = np.where(np.logical_and(dist > 0.001, grid_mask == 1))[0] good_pts = np.where(np.logical_and(dist < 0.001, grid_mask == 1))[0] for i in bad_pts: didx = np.sqrt((sta_lon[i] - sta_lon[good_pts])**2 + (sta_lat[i] - sta_lat[good_pts])**2).argmin() index = good_pts[didx] sta_h[i] = sta_h[index] sta_angle[i] = sta_angle[index] sta_lon[i] = sta_lon[index] sta_lat[i] = sta_lat[index] sta_zeta[:, i] = sta_zeta[:, index] sta_ubar[:, i] = sta_ubar[:, index] sta_vbar[:, i] = sta_vbar[:, index] sta_temp[:, i, :] = sta_temp[:, index, :] sta_salt[:, i, :] = sta_salt[:, index, :] sta_u[:, i, :] = sta_u[:, index, :] sta_v[:, i, :] = sta_v[:, index, :] # Construct the boundaries: a dictionary of boundary side and two element # array whether the u[0] or v[1] dimensions need to be averaged sides = { "north": [True, False], "south": [True, False], "east": [False, True], "west": [False, True] } delta_angle = sta_angle - grid_angle sta_ubar, sta_vbar = seapy.rotate(sta_ubar, sta_vbar, delta_angle) sta_u, sta_v = seapy.rotate(sta_u, sta_v, np.tile(delta_angle, (sta_u.shape[-1], 1)).T) # Set up the parameters for depth-interpolated wght = 5 nx = 3 ny = 9 # Build a non-extrapolating field to interpolate. Generate the # position and depth def __expand_field(x): shp = x.shape y = np.zeros((shp[0] + 2, shp[1] + 2)) y[1:-1, 1:-1] = x y[1:-1, 0] = x[:, 0] y[1:-1, -1] = x[:, -1] y[0, :] = y[1, :] y[-1, :] = y[-2, :] return y for side in sides: print(side) # Masks sta_ocean = np.where(sta_mask[bry[side]] == 1)[0] ocean = np.where(grid_mask[bry[side]] == 1)[0] # If we have a masked boundary, skip it if not np.any(ocean): continue # 1) Zeta ncbry.variables["zeta_" + side][:, ocean] = sta_zeta[:, bry[side]][:, ocean] # 2) Ubar if sides[side][0]: ncbry.variables["ubar_" + side][:] = 0.5 * (sta_ubar[:, bry[side][0:-1]] + sta_ubar[:, bry[side][1:]]) else: ncbry.variables["ubar_" + side][:] = sta_ubar[:, bry[side]] # 3) Vbar if sides[side][1]: ncbry.variables["vbar_" + side][:] = 0.5 * (sta_vbar[:, bry[side][0:-1]] + sta_vbar[:, bry[side][1:]]) else: ncbry.variables["vbar_" + side][:] = sta_vbar[:, bry[side]] # For 3D variables, we need to loop through time and interpolate # onto the child grid. Construct the distances x = np.zeros(len(bry[side])) x[1:] = np.cumsum( seapy.earth_distance(grid_lon[bry[side][0:-1]], grid_lat[bry[side][0:-1]], grid_lon[bry[side][1:]], grid_lat[bry[side][1:]])) sta_x = seapy.adddim(x, len(sta_s_rho)) x = seapy.adddim(x, len(grid.s_rho)) for n, t in seapy.progressbar.progress(enumerate(statime), statime.size): sta_depth = seapy.roms.depth(sta_vt, sta_h[bry[side]], sta_hc, sta_s_rho, sta_cs_r, sta_zeta[n, bry[side]]) depth = seapy.roms.depth(grid.vtransform, grid_h[bry[side]], grid.hc, grid.s_rho, grid.cs_r, sta_zeta[n, bry[side]]) in_x = __expand_field(sta_x[:, sta_ocean]) in_x[:, 0] = in_x[:, 0] - 3600 in_x[:, -1] = in_x[:, -1] + 3600 in_depth = __expand_field(sta_depth[:, sta_ocean]) in_depth[0, :] = in_depth[0, :] - 1000 in_depth[-1, :] = in_depth[-1, :] + 10 # 4) Temp in_data = __expand_field( np.transpose(sta_temp[n, bry[side], :][sta_ocean, :])) ncbry.variables["temp_" + side][n, :] = 0.0 ncbry.variables["temp_" + side][n, :, ocean], pmap = seapy.oa.oasurf( in_x, in_depth, in_data, x[:, ocean], depth[:, ocean], nx=nx, ny=ny, weight=wght) # 5) Salt in_data = __expand_field( np.transpose(sta_salt[n, bry[side], :][sta_ocean, :])) ncbry.variables["salt_" + side][n, :] = 0.0 ncbry.variables["salt_" + side][n, :, ocean], pmap = seapy.oa.oasurf( in_x, in_depth, in_data, x[:, ocean], depth[:, ocean], pmap=pmap, nx=nx, ny=ny, weight=wght) # 6) U in_data = __expand_field( np.transpose(sta_u[n, bry[side], :][sta_ocean, :])) data = np.zeros(x.shape) data[:, ocean], pmap = seapy.oa.oasurf(in_x, in_depth, in_data, x[:, ocean], depth[:, ocean], pmap=pmap, nx=nx, ny=ny, weight=wght) if sides[side][0]: ncbry.variables["u_" + side][n, :] = 0.5 * (data[:, 0:-1] + data[:, 1:]) else: ncbry.variables["u_" + side][n, :] = data # 7) V in_data = __expand_field( np.transpose(sta_v[n, bry[side], :][sta_ocean, :])) data = data * 0 data[:, ocean], pmap = seapy.oa.oasurf(in_x, in_depth, in_data, x[:, ocean], depth[:, ocean], pmap=pmap, nx=nx, ny=ny, weight=wght) if sides[side][1]: ncbry.variables["v_" + side][n, :] = 0.5 * (data[:, 0:-1] + data[:, 1:]) else: ncbry.variables["v_" + side][n, :] = data ncbry.sync() ncbry.close() pass