def convert_file(self, file, title="SeaGlider Obs"): """ Load a SeaGlider .pro file and convert into an obs structure """ import re # Load the text file. All data goes into the pro dictionary # as defined by dtype. The header information needs to be parsed with open(file) as myfile: header = [myfile.readline() for i in range(19)] pro = np.loadtxt(myfile, self.dtype, delimiter=',', comments='%') # Parse the header information parser = re.compile('^%(\w+): (.*)$') params = {} for line in header: try: opt = parser.findall(line) params[opt[0][0]] = opt[0][1] except: pass # Determine the needed information from the headers glider_name = "GLIDER" if params.get("glider", None) is None else \ "GLIDER_SG" + params["glider"] provenance = seapy.roms.obs.asprovenance(glider_name) try: date = [int(s) for s in re.findall('([\d]{2})\s', params["start"])] start_time = datetime.datetime.strptime(params["start"].strip(), "%m %d 1%y %H %M %S") dtime = (start_time - self.epoch).total_seconds() / 86400 except: raise ValueError("date format incorrect in file: " + file) # Make sure that the GPS fix isn't screwy if self.grid.east(): pro["lon"][pro["lon"] < 0] += 360 dist = seapy.earth_distance(pro["lon"][0], pro["lat"][0], pro["lon"][-1], pro["lat"][-1]) velocity = dist / pro["time"][-1] if velocity > 2: warn("WARNING: GPS fix is incorrect for " + file) return None # Build the data with masked entries temp = np.ma.masked_outside(pro["temp"], self.temp_limits[0], self.temp_limits[1]) salt = np.ma.masked_outside(pro["salt"], self.salt_limits[0], self.salt_limits[1]) depth = np.ma.masked_greater(-pro["depth"], self.depth_limit) good = ~np.ma.getmaskarray(depth) # Grid it data = [seapy.roms.obs.raw_data("TEMP", provenance, temp[good], None, self.temp_error), seapy.roms.obs.raw_data("SALT", provenance, salt[good], None, self.salt_error)] return seapy.roms.obs.gridder(self.grid, pro["time"][good] / 86400 + dtime, pro["lon"][good], pro["lat"][good], depth.compressed(), data, self.dt, title)
def calc_latlon(llcrnrlat, llcrnrlon, reseta, resxi=None, rotate=0): """ Generate arrays for latitude and longitude for use in creating simple grids. NOTE: You can specify variational resolutions and rotations; however, this uses a simple algorithm to step along from the origin, and discrepencies are averaged together. THEREFORE, if you are not specifying inconsistent resolutions or rotations within single row or columns, you may get results slightly different than specified. Parameters ---------- llcrnrlat : float, Latitude for the lower, left of the grid llcrnrlon : float, Longitude for the lower, left of the grid reseta : ndarray, Resolution in meters in the eta-direction of each grid cell. A 2D array that is the horizontal size of the grid (e.g., resolution.shape = (100,70)) and the values stored are the desired resolution of the grid cell in m. Hence, a 100 x 70 array of ones would create a grid that is 100 in the eta-direction, 70 in the xi-direction, and a constant resolution of 1km. If the lat and lon is specified as arrays, this is optional. resxi : ndarray, optional, Resolution in meters in the xi-direction of each grid cell. This is the same as reseta; however, it is optional, and is set to the same as reseta if not specified. rotate : float or ndarray, Amount to rotate the grid in degrees. If given as a scalar, the entire grid is rotated at the same angle; otherwise, the grid my have curvilinear shape. The angle is geometric (counter-clockwise). Returns ------- lat, lon : ndarray Two arrays of size resolution containing the computed lat and lons Examples -------- Create a grid of 1km resolution in both eta and xi, rotated toward the SE by 33 degrees, with the lower left point at 20N, 150E: >>> res = np.ones((100,70)) * 1000 >>> lat, lon = calc_latlon(20, 150, res, rotate=-33) """ # Set up the resolutions if resxi is None: resxi = reseta else: if resxi.shape != reseta.shape: raise AttributeError( "xi and eta resolutions must be same size array") # Since we are taking steps, average the values together as we # can't take the final step reseta[:-1, :] = 0.5 * (reseta[:-1, :] + reseta[1:, :]) resxi[:, :-1] = 0.5 * (resxi[:, :-1] + resxi[:, 1:]) # Set up the rotations if np.isscalar(rotate): rotate = np.ones(reseta.shape) * rotate rotate = np.radians(rotate) # Set up the lat and lon arrays lat = np.ones(reseta.shape) * np.nan lon = np.ones(reseta.shape) * np.nan lat[0, 0] = llcrnrlat lon[0, 0] = llcrnrlon pi2 = np.pi / 2.0 # Loop over each row, and within the row, step along in the # xi-direction before moving to the next row for j in seapy.progressbar.progress(range(lat.shape[0] - 1)): for i in range(lat.shape[1] - 1): # Compute the local deltas dlat = seapy.earth_distance( lon[j, i], lat[j, i] - 0.5, lon[j, i], lat[j, i] + 0.5) dlon = seapy.earth_distance( lon[j, i] - 0.5, lat[j, i], lon[j, i] + 0.5, lat[j, i]) # Compute how far to step in xi xdx = resxi[j, i + 1] * np.cos(rotate[j, i]) / dlon xdy = resxi[j, i + 1] * np.sin(rotate[j, i]) / dlat # Take the step lat[j, i + 1] = lat[j, i] + xdy lon[j, i + 1] = lon[j, i] + xdx # Compute how far to step in eta edx = reseta[j + 1, i] * np.cos(rotate[j, i] + pi2) / dlon edy = reseta[j + 1, i] * np.sin(rotate[j, i] + pi2) / dlat # Take the step lat[j + 1, i] = lat[j, i] + edy lon[j + 1, i] = lon[j, i] + edx lon[-1, -1] = lon[-1, -2] + xdx lat[-1, -1] = lat[-2, -1] + edy return lat, lon
def transect(lon, lat, depth, data, nx=200, nz=40, z=None): """ Generate an equidistant transect from data at varying depths. Can be used to plot a slice of model or observational data. Parameters ---------- lat: array n-dimensional array with latitude of points lon: array n-dimensional array with longitude of points depth: array [k,n] dimensional array of the depths for all k-layers at each n point data: array [k,n] dimensional array of values nx: int, optional number of horizontal points desired in the transect nz: int, optional number of vertical points desired in the transect z: array, optional list of depths to use if you do not want equidistant depths Returns ------- x: array x-location values in [m] along transect z: array depth values in [m] of the new transect vals: np.ma.array data values of the new transect with masked values Examples -------- Generate series of transects from ROMS output >>> nc = seapy.netcdf('roms_his.nc') >>> grid = seapy.model.asgrid(nc) >>> data = nc.variables['salt'][:,:,150,:] >>> shp = (data.shape[0], 50, 400) >>> transect = np.zeros(shp) >>> for i in range(shp[0]): >>> x, z, d = \ >>> seapy.roms.analysis.transect(grid.lon_rho[150,:], >>> grid.lat_rho[150,:], >>> grid.depth_rho[:,150,:], >>> data[i,:,:], nx=shp[2], >>> nz=shp[1]) >>> transect[i,:,:] = d.filled(np.nan) >>> nc.close() >>> plt.pcolormesh(x/1000, z, transect[0, :, :]) """ from scipy.interpolate import griddata depth = np.atleast_2d(depth) data = np.ma.atleast_2d(data).filled(np.mean(data)) lon = np.atleast_1d(lon) lat = np.atleast_1d(lat) # Generate the depths depth[depth > 0] *= -1 if z is None: z = np.linspace(depth.min() - 2, depth.max(), nz) else: z[z > 0] *= -1 nz = len(z) dz = np.abs(np.diff(z).mean()) # Determine the distance between points and the weighting to apply dist = np.hstack( ([0], seapy.earth_distance(lon[0], lat[0], lon[1:], lat[1:]))) dx = np.diff(dist).mean() zscale = np.maximum(1, 10**int(np.log10(dx / dz))) dx /= zscale x = np.linspace(0, dist.max(), nx) # All arrays have to be the same size xx, zz = np.meshgrid(x / zscale, z) # For the source data, we don't want to extrpolate, # so make the data go from the surface to twice its # depth. zl = np.argsort(depth[:, 0]) dep = np.vstack((np.ones( (1, depth.shape[1])) * 2 * depth.min(), depth[zl, :], np.zeros((1, depth.shape[1])))) # repeat the same data at the top and bottom dat = np.vstack((data[zl[0], :], data[zl], data[zl[-1], :])) dist = np.tile(dist.T, [dep.shape[0], 1]) / zscale # Find the bottom indices to create a mask for nodata/land idx = np.interp(xx[0, :], dist[0, :], np.interp(depth.min(axis=0), z, np.arange(nz))).astype(int) mask = np.arange(nz)[:, np.newaxis] <= idx # Interpolate ndat = np.ma.array(griddata((dist.ravel(), dep.ravel()), dat.ravel(), (xx.ravel(), zz.ravel()), method='cubic').reshape(xx.shape), mask=mask) # Return everything return x, z, ndat
def set_dims(self): """ Compute the dimension attributes of the grid based upon the information provided. Parameters ---------- None Returns ------- None : sets attributes in grid """ # If C-Grid, set the dimensions for consistency if self.cgrid: self.eta_rho = self.ln self.eta_u = self.ln self.eta_v = self.ln - 1 self.xi_rho = self.lm self.xi_u = self.lm - 1 self.xi_v = self.lm # Set the number of layers if "n" not in self.__dict__: if "s_rho" in self.__dict__: self.n = int(self.s_rho.size) elif "z" in self.__dict__: self.n = int(self.z.size) else: self.n = 1 self.z = np.zeros(self.lat_rho.shape) else: self.n = int(self.n) # Generate the u- and v-grids if ("lat_u" or "lon_u") not in self.__dict__: if self.cgrid: self.lat_u = 0.5 * \ (self.lat_rho[:, 1:] - self.lat_rho[:, 0:-1]) self.lon_u = 0.5 * \ (self.lon_rho[:, 1:] - self.lon_rho[:, 0:-1]) else: self.lat_u = self.lat_rho self.lon_u = self.lon_rho if ("lat_v" or "lon_v") not in self.__dict__: if self.cgrid: self.lat_v = 0.5 * \ (self.lat_rho[1:, :] - self.lat_rho[0:-1, :]) self.lon_v = 0.5 * \ (self.lon_rho[1:, :] - self.lon_rho[0:-1, :]) else: self.lat_v = self.lat_rho self.lon_v = self.lon_rho if "mask_rho" in self.__dict__: if "mask_u" not in self.__dict__: if self.cgrid: self.mask_u = self.mask_rho[:, 1:] * self.mask_rho[:, 0:-1] else: self.mask_u = self.mask_rho if "mask_v" not in self.__dict__: if self.cgrid: self.mask_v = self.mask_rho[1:, :] * self.mask_rho[0:-1, :] else: self.mask_v = self.mask_rho # Compute the resolution if "pm" in self.__dict__: self.dm = 1.0 / self.pm else: self.dm = np.ones(self.lon_rho.shape, dtype=np.float32) self.dm[:, 0:-1] = seapy.earth_distance( self.lon_rho[:, 1:], self.lat_rho[:, 1:], self.lon_rho[:, 0:-1], self.lat_rho[:, 0:-1]).astype(np.float32) self.dm[:, -1] = self.dm[:, -2] self.pm = 1.0 / self.dm if "pn" in self.__dict__: self.dn = 1.0 / self.pn else: self.dn = np.ones(self.lat_rho.shape, dtype=np.float32) self.dn[0:-1, :] = seapy.earth_distance( self.lon_rho[1:, :], self.lat_rho[1:, :], self.lon_rho[0:-1, :], self.lat_rho[0:-1, :]).astype(np.float32) self.dn[-1, :] = self.dn[-2, :] self.pn = 1.0 / self.dn # Compute the Coriolis if "f" not in self.__dict__: omega = 2 * np.pi * seapy.secs2day self.f = 2 * omega * np.sin(np.radians(self.lat_rho)) # Set the grid index coordinates self.I, self.J = np.meshgrid(np.arange(0, self.lm), np.arange(0, self.ln))
def create_grid(grid_file, lat, lon): """ Create a new, basic grid. This will construct the ROMS grid netcdf file given a set of latitude and longitude coordinates for the rho-grid. The coordinates can be spaced however specified, and the grid will be created; however, this does not guarantee the grid will be valid. After the grid is created, the bathymetry and mask will need to be generated independently. Parameters ---------- grid_file : string, Name of the grid file to create. NOTE: this file will be overwritten. lat : ndarray, latitude of the grid cells. The array must be the size of the desired grid. lon : ndarray, longitude of the grid cells. The array must be the size of the desired grid. Returns ------- netCDF4 : The netcdf object of the new grid Examples -------- To create a basic, evenly spaced grid: >>> lat = np.linspace(10,20,0.25) >>> lon = np.linspace(-100,-80,0.25) >>> lon, lat = np.meshgrid(lon, lat) >>> create_grid('mygrid.nc', lat, lon) To create more advanced grids, simply generate the 2D arrays of lat and lon in the manner you want your cells and call create_grid: >>> create_grid('mygrid.nc', lat, lon) """ # Put lat/lon into proper arrays lat = np.atleast_2d(lat) lon = np.atleast_2d(lon) if lat.shape != lon.shape: raise AttributeError("lat and lon shapes are not equal") # Calculate the angle between the points angle = np.zeros(lat.shape) angle[:, :-1] = seapy.earth_angle(lon[:, :-1], lat[:, :-1], lon[:, 1:], lat[:, 1:]) angle[:, -1] = angle[:, -2] # Calculate distances/parameters f = 2.0 * 7.2921150e-5 * np.sin(lat * np.pi / 180.0) dx = np.zeros(f.shape) dy = np.zeros(f.shape) dx[:, 1:] = seapy.earth_distance( lon[:, 1:], lat[:, 1:], lon[:, :-1], lat[:, :-1]) dy[1:, :] = seapy.earth_distance( lon[1:, :], lat[1:, :], lon[:-1, :], lat[:-1, :]) dx[:, 0] = dx[:, 1] dy[0, :] = dy[1, :] pm = 1.0 / dx pn = 1.0 / dy dndx = np.zeros(dx.shape) dmde = np.zeros(dx.shape) dndx[:, 1:-1] = 0.5 * (dy[:, 2:] - dy[:, :-2]) dmde[1:-1, :] = 0.5 * (dx[2:, :] - dx[:-2, :]) xl = seapy.earth_distance( np.min(lon), np.mean(lat), np.max(lon), np.mean(lat)) el = seapy.earth_distance( np.mean(lon), np.min(lat), np.mean(lon), np.max(lat)) # Generate rho-grid coordinates x_rho = np.zeros(lat.shape) y_rho = np.zeros(lat.shape) x_rho[:, 1:] = seapy.earth_distance( lon[:, :-1], lat[:, :-1], lon[:, 1:], lat[:, 1:]) x_rho = np.cumsum(x_rho, axis=1) y_rho[1:, :] = seapy.earth_distance( lon[:-1, :], lat[:-1, :], lon[1:, :], lat[1:, :]) y_rho = np.cumsum(y_rho, axis=0) # Create u-grid lat_u = 0.5 * (lat[:, 1:] + lat[:, :-1]) lon_u = 0.5 * (lon[:, 1:] + lon[:, 0:-1]) x_u = np.zeros(lat_u.shape) y_u = np.zeros(lat_u.shape) x_u[:, 1:] = seapy.earth_distance( lon_u[:, :-1], lat_u[:, :-1], lon_u[:, 1:], lat_u[:, 1:]) x_u = np.cumsum(x_u, axis=1) y_u[1:, :] = seapy.earth_distance( lon_u[:-1, :], lat_u[:-1, :], lon_u[1:, :], lat_u[1:, :]) y_u = np.cumsum(y_u, axis=0) # Create v-grid lat_v = 0.5 * (lat[1:, :] + lat[0:-1, :]) lon_v = 0.5 * (lon[1:, :] + lon[0:-1, :]) x_v = np.zeros(lat_v.shape) y_v = np.zeros(lat_v.shape) x_v[:, 1:] = seapy.earth_distance( lon_v[:, :-1], lat_v[:, :-1], lon_v[:, 1:], lat_v[:, 1:]) x_v = np.cumsum(x_v, axis=1) y_v[1:, :] = seapy.earth_distance( lon_v[:-1, :], lat_v[:-1, :], lon_v[1:, :], lat_v[1:, :]) y_v = np.cumsum(y_v, axis=0) # Create psi-grid lat_psi = lat_v[:, :-1] lon_psi = lon_u[:-1, :] x_psi = np.zeros(lat_psi.shape) y_psi = np.zeros(lat_psi.shape) x_psi[:, 1:] = seapy.earth_distance( lon_psi[:, :-1], lat_psi[:, :-1], lon_psi[:, 1:], lat_psi[:, 1:]) x_psi = np.cumsum(x_psi, axis=1) y_psi[1:, :] = seapy.earth_distance( lon_psi[:-1, :], lat_psi[:-1, :], lon_psi[1:, :], lat_psi[1:, :]) y_psi = np.cumsum(y_psi, axis=0) # Create the new grid nc = seapy.roms.ncgen.create_grid( grid_file, lat.shape[0], lat.shape[1], clobber=True) nc.variables["xl"][:] = xl nc.variables["el"][:] = el nc.variables["spherical"][:] = 1 nc.variables["f"][:] = f nc.variables["pm"][:] = pm nc.variables["pn"][:] = pn nc.variables["dndx"][:] = dndx nc.variables["dmde"][:] = dmde nc.variables["x_rho"][:] = x_rho nc.variables["y_rho"][:] = y_rho nc.variables["x_psi"][:] = x_psi nc.variables["y_psi"][:] = y_psi nc.variables["x_u"][:] = x_u nc.variables["y_u"][:] = y_u nc.variables["x_v"][:] = x_v nc.variables["y_v"][:] = y_v nc.variables["lat_rho"][:] = lat nc.variables["lon_rho"][:] = lon nc.variables["lat_psi"][:] = lat_psi nc.variables["lon_psi"][:] = lon_psi nc.variables["lat_u"][:] = lat_u nc.variables["lon_u"][:] = lon_u nc.variables["lat_v"][:] = lat_v nc.variables["lon_v"][:] = lon_v nc.variables["N"][:] = 1 nc.variables["mask_rho"][:] = np.ones(lat.shape) nc.variables["mask_u"][:] = np.ones(lat_u.shape) nc.variables["mask_v"][:] = np.ones(lat_v.shape) nc.variables["mask_psi"][:] = np.ones(lat_psi.shape) nc.variables["angle"][:] = angle nc.variables["rdrag"][:] = np.ones(lon.shape) * 0.0003 nc.variables["rdrag2"][:] = np.ones(lon.shape) * 0.003 nc.variables["ZoBot"][:] = np.ones(lon.shape) * 0.02 nc.sync() return nc
def calc_latlon(llcrnrlat, llcrnrlon, reseta, resxi=None, rotate=0): """ Generate arrays for latitude and longitude for use in creating simple grids. NOTE: You can specify variational resolutions and rotations; however, this uses a simple algorithm to step along from the origin, and discrepencies are averaged together. THEREFORE, if you are not specifying inconsistent resolutions or rotations within single row or columns, you may get results slightly different than specified. Parameters ---------- llcrnrlat : float, Latitude for the lower, left of the grid llcrnrlon : float, Longitude for the lower, left of the grid reseta : ndarray, Resolution in meters in the eta-direction of each grid cell. A 2D array that is the horizontal size of the grid (e.g., resolution.shape = (100,70)) and the values stored are the desired resolution of the grid cell in m. Hence, a 100 x 70 array of ones would create a grid that is 100 in the eta-direction, 70 in the xi-direction, and a constant resolution of 1km. If the lat and lon is specified as arrays, this is optional. resxi : ndarray, optional, Resolution in meters in the xi-direction of each grid cell. This is the same as reseta; however, it is optional, and is set to the same as reseta if not specified. rotate : float or ndarray, Amount to rotate the grid in degrees. If given as a scalar, the entire grid is rotated at the same angle; otherwise, the grid my have curvilinear shape. The angle is geometric (counter-clockwise). Returns ------- lat, lon : ndarray Two arrays of size resolution containing the computed lat and lons Examples -------- Create a grid of 1km resolution in both eta and xi, rotated toward the SE by 33 degrees, with the lower left point at 20N, 150E: >>> res = np.ones((100,70)) * 1000 >>> lat, lon = calc_latlon(20, 150, res, rotate=-33) """ # Set up the resolutions if resxi is None: resxi = reseta else: if resxi.shape != reseta.shape: raise AttributeError( "xi and eta resolutions must be same size array") # Since we are taking steps, average the values together as we # can't take the final step reseta[:-1, :] = 0.5 * (reseta[:-1, :] + reseta[1:, :]) resxi[:, :-1] = 0.5 * (resxi[:, :-1] + resxi[:, 1:]) # Set up the rotations if np.isscalar(rotate): rotate = np.ones(reseta.shape) * rotate rotate = np.radians(rotate) # Set up the lat and lon arrays lat = np.ones(reseta.shape) * np.nan lon = np.ones(reseta.shape) * np.nan lat[0, 0] = llcrnrlat lon[0, 0] = llcrnrlon pi2 = np.pi / 2.0 # Loop over each row, and within the row, step along in the # xi-direction before moving to the next row for j in seapy.progressbar.progress(range(lat.shape[0] - 1)): for i in range(lat.shape[1] - 1): # Compute the local deltas dlat = seapy.earth_distance(lon[j, i], lat[j, i] - 0.5, lon[j, i], lat[j, i] + 0.5) dlon = seapy.earth_distance(lon[j, i] - 0.5, lat[j, i], lon[j, i] + 0.5, lat[j, i]) # Compute how far to step in xi xdx = resxi[j, i + 1] * np.cos(rotate[j, i]) / dlon xdy = resxi[j, i + 1] * np.sin(rotate[j, i]) / dlat # Take the step lat[j, i + 1] = lat[j, i] + xdy lon[j, i + 1] = lon[j, i] + xdx # Compute how far to step in eta edx = reseta[j + 1, i] * np.cos(rotate[j, i] + pi2) / dlon edy = reseta[j + 1, i] * np.sin(rotate[j, i] + pi2) / dlat # Take the step lat[j + 1, i] = lat[j, i] + edy lon[j + 1, i] = lon[j, i] + edx lon[-1, -1] = lon[-1, -2] + xdx lat[-1, -1] = lat[-2, -1] + edy return lat, lon
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 create_grid(grid_file, lat, lon): """ Create a new, basic grid. This will construct the ROMS grid netcdf file given a set of latitude and longitude coordinates for the rho-grid. The coordinates can be spaced however specified, and the grid will be created; however, this does not guarantee the grid will be valid. After the grid is created, the bathymetry and mask will need to be generated independently. Parameters ---------- grid_file : string, Name of the grid file to create. NOTE: this file will be overwritten. lat : ndarray, latitude of the grid cells. The array must be the size of the desired grid. lon : ndarray, longitude of the grid cells. The array must be the size of the desired grid. Returns ------- netCDF4 : The netcdf object of the new grid Examples -------- To create a basic, evenly spaced grid: >>> lat = np.linspace(10,20,0.25) >>> lon = np.linspace(-100,-80,0.25) >>> lon, lat = np.meshgrid(lon, lat) >>> create_grid('mygrid.nc', lat, lon) To create more advanced grids, simply generate the 2D arrays of lat and lon in the manner you want your cells and call create_grid: >>> create_grid('mygrid.nc', lat, lon) """ # Put lat/lon into proper arrays lat = np.atleast_2d(lat) lon = np.atleast_2d(lon) if lat.shape != lon.shape: raise AttributeError("lat and lon shapes are not equal") # Calculate the angle between the points angle = np.zeros(lat.shape) angle[:, :-1] = seapy.earth_angle(lon[:, :-1], lat[:, :-1], lon[:, 1:], lat[:, 1:]) angle[:, -1] = angle[:, -2] # Calculate distances/parameters f = 2.0 * 7.2921150e-5 * np.sin(lat * np.pi / 180.0) dx = np.zeros(f.shape) dy = np.zeros(f.shape) dx[:, 1:] = seapy.earth_distance(lon[:, 1:], lat[:, 1:], lon[:, :-1], lat[:, :-1]) dy[1:, :] = seapy.earth_distance(lon[1:, :], lat[1:, :], lon[:-1, :], lat[:-1, :]) dx[:, 0] = dx[:, 1] dy[0, :] = dy[1, :] pm = 1.0 / dx pn = 1.0 / dy dndx = np.zeros(dx.shape) dmde = np.zeros(dx.shape) dndx[:, 1:-1] = 0.5 * (dy[:, 2:] - dy[:, :-2]) dmde[1:-1, :] = 0.5 * (dx[2:, :] - dx[:-2, :]) xl = seapy.earth_distance(np.min(lon), np.mean(lat), np.max(lon), np.mean(lat)) el = seapy.earth_distance(np.mean(lon), np.min(lat), np.mean(lon), np.max(lat)) # Generate rho-grid coordinates x_rho = np.zeros(lat.shape) y_rho = np.zeros(lat.shape) x_rho[:, 1:] = seapy.earth_distance(lon[:, :-1], lat[:, :-1], lon[:, 1:], lat[:, 1:]) x_rho = np.cumsum(x_rho, axis=1) y_rho[1:, :] = seapy.earth_distance(lon[:-1, :], lat[:-1, :], lon[1:, :], lat[1:, :]) y_rho = np.cumsum(y_rho, axis=0) # Create u-grid lat_u = 0.5 * (lat[:, 1:] + lat[:, :-1]) lon_u = 0.5 * (lon[:, 1:] + lon[:, 0:-1]) x_u = np.zeros(lat_u.shape) y_u = np.zeros(lat_u.shape) x_u[:, 1:] = seapy.earth_distance(lon_u[:, :-1], lat_u[:, :-1], lon_u[:, 1:], lat_u[:, 1:]) x_u = np.cumsum(x_u, axis=1) y_u[1:, :] = seapy.earth_distance(lon_u[:-1, :], lat_u[:-1, :], lon_u[1:, :], lat_u[1:, :]) y_u = np.cumsum(y_u, axis=0) # Create v-grid lat_v = 0.5 * (lat[1:, :] + lat[0:-1, :]) lon_v = 0.5 * (lon[1:, :] + lon[0:-1, :]) x_v = np.zeros(lat_v.shape) y_v = np.zeros(lat_v.shape) x_v[:, 1:] = seapy.earth_distance(lon_v[:, :-1], lat_v[:, :-1], lon_v[:, 1:], lat_v[:, 1:]) x_v = np.cumsum(x_v, axis=1) y_v[1:, :] = seapy.earth_distance(lon_v[:-1, :], lat_v[:-1, :], lon_v[1:, :], lat_v[1:, :]) y_v = np.cumsum(y_v, axis=0) # Create psi-grid lat_psi = lat_v[:, :-1] lon_psi = lon_u[:-1, :] x_psi = np.zeros(lat_psi.shape) y_psi = np.zeros(lat_psi.shape) x_psi[:, 1:] = seapy.earth_distance(lon_psi[:, :-1], lat_psi[:, :-1], lon_psi[:, 1:], lat_psi[:, 1:]) x_psi = np.cumsum(x_psi, axis=1) y_psi[1:, :] = seapy.earth_distance(lon_psi[:-1, :], lat_psi[:-1, :], lon_psi[1:, :], lat_psi[1:, :]) y_psi = np.cumsum(y_psi, axis=0) # Create the new grid nc = seapy.roms.ncgen.create_grid(grid_file, lat.shape[0], lat.shape[1], clobber=True) nc.variables["xl"][:] = xl nc.variables["el"][:] = el nc.variables["spherical"][:] = 1 nc.variables["f"][:] = f nc.variables["pm"][:] = pm nc.variables["pn"][:] = pn nc.variables["dndx"][:] = dndx nc.variables["dmde"][:] = dmde nc.variables["x_rho"][:] = x_rho nc.variables["y_rho"][:] = y_rho nc.variables["x_psi"][:] = x_psi nc.variables["y_psi"][:] = y_psi nc.variables["x_u"][:] = x_u nc.variables["y_u"][:] = y_u nc.variables["x_v"][:] = x_v nc.variables["y_v"][:] = y_v nc.variables["lat_rho"][:] = lat nc.variables["lon_rho"][:] = lon nc.variables["lat_psi"][:] = lat_psi nc.variables["lon_psi"][:] = lon_psi nc.variables["lat_u"][:] = lat_u nc.variables["lon_u"][:] = lon_u nc.variables["lat_v"][:] = lat_v nc.variables["lon_v"][:] = lon_v nc.variables["N"][:] = 1 nc.variables["mask_rho"][:] = np.ones(lat.shape) nc.variables["mask_u"][:] = np.ones(lat_u.shape) nc.variables["mask_v"][:] = np.ones(lat_v.shape) nc.variables["mask_psi"][:] = np.ones(lat_psi.shape) nc.variables["angle"][:] = angle nc.variables["rdrag"][:] = np.ones(lon.shape) * 0.0003 nc.variables["rdrag2"][:] = np.ones(lon.shape) * 0.003 nc.variables["visc_factor"][:] = np.ones(lon.shape) nc.variables["diff_factor"][:] = np.ones(lon.shape) nc.variables["ZoBot"][:] = np.ones(lon.shape) * 0.02 nc.sync() return nc
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 set_dims(self): """ Compute the dimension attributes of the grid based upon the information provided. Parameters ---------- None Returns ------- None : sets attributes in grid """ # If C-Grid, set the dimensions for consistency if self.cgrid: self.eta_rho = self.ln self.eta_u = self.ln self.eta_v = self.ln - 1 self.xi_rho = self.lm self.xi_u = self.lm - 1 self.xi_v = self.lm # Set the number of layers if "n" not in self.__dict__: if "s_rho" in self.__dict__: self.n = int(self.s_rho.size) elif "z" in self.__dict__: self.n = int(self.z.size) else: self.n = 1 self.z = np.zeros(self.lat_rho.shape) else: self.n = int(self.n) # Generate the u- and v-grids if ("lat_u" or "lon_u") not in self.__dict__: if self.cgrid: self.lat_u = 0.5 * \ (self.lat_rho[:, 1:] - self.lat_rho[:, 0:-1]) self.lon_u = 0.5 * \ (self.lon_rho[:, 1:] - self.lon_rho[:, 0:-1]) else: self.lat_u = self.lat_rho self.lon_u = self.lon_rho if ("lat_v" or "lon_v") not in self.__dict__: if self.cgrid: self.lat_v = 0.5 * \ (self.lat_rho[1:, :] - self.lat_rho[0:-1, :]) self.lon_v = 0.5 * \ (self.lon_rho[1:, :] - self.lon_rho[0:-1, :]) else: self.lat_v = self.lat_rho self.lon_v = self.lon_rho if "mask_rho" in self.__dict__: if "mask_u" not in self.__dict__: if self.cgrid: self.mask_u = self.mask_rho[:, 1:] * self.mask_rho[:, 0:-1] else: self.mask_u = self.mask_rho if "mask_v" not in self.__dict__: if self.cgrid: self.mask_v = self.mask_rho[1:, :] * self.mask_rho[0:-1, :] else: self.mask_v = self.mask_rho # Compute the resolution if "pm" in self.__dict__: self.dm = 1.0 / self.pm else: self.dm = np.ones(self.lon_rho.shape, dtype=np.float32) self.dm[:, 0:-1] = seapy.earth_distance(self.lon_rho[:, 1:], self.lat_rho[:, 1:], self.lon_rho[:, 0:-1], self.lat_rho[:, 0:-1]).astype(np.float32) self.dm[:, -1] = self.dm[:, -2] if "pn" in self.__dict__: self.dn = 1.0 / self.pn else: self.dn = np.ones(self.lat_rho.shape, dtype=np.float32) self.dn[0:-1, :] = seapy.earth_distance(self.lon_rho[1:, :], self.lat_rho[1:, :], self.lon_rho[0:-1, :], self.lat_rho[0:-1, :]).astype(np.float32) self.dn[-1, :] = self.dn[-2, :] # Compute the Coriolis if "f" not in self.__dict__: omega = 2 * np.pi * seapy.secs2day self.f = 2 * omega * np.sin(np.radians(self.lat_rho)) # Set the grid index coordinates self.I, self.J = np.meshgrid( np.arange(0, self.lm), np.arange(0, self.ln))
def transect(lon, lat, depth, data, nx=200, nz=40, z=None): """ Generate an equidistant transect from data at varying depths. Can be used to plot a slice of model or observational data. Parameters ---------- lat: array n-dimensional array with latitude of points lon: array n-dimensional array with longitude of points depth: array [k,n] dimensional array of the depths for all k-layers at each n point data: array [k,n] dimensional array of values nx: int, optional number of horizontal points desired in the transect nz: int, optional number of vertical points desired in the transect z: array, optional list of depths to use if you do not want equidistant depths Returns ------- x: array x-location values in [m] along transect z: array depth values in [m] of the new transect vals: np.ma.array data values of the new transect with masked values Examples -------- Generate series of transects from ROMS output >>> nc = seapy.netcdf('roms_his.nc') >>> grid = seapy.model.asgrid(nc) >>> data = nc.variables['salt'][:,:,150,:] >>> shp = (data.shape[0], 50, 400) >>> transect = np.zeros(shp) >>> for i in range(shp[0]): >>> x, z, d = \ >>> seapy.roms.analysis.transect(grid.lon_rho[150,:], >>> grid.lat_rho[150,:], >>> grid.depth_rho[:,150,:], >>> data[i,:,:], nx=shp[2], >>> nz=shp[1]) >>> transect[i,:,:] = d.filled(np.nan) >>> nc.close() >>> plt.pcolormesh(x/1000, z, transect[0, :, :]) """ from scipy.interpolate import griddata depth = np.atleast_2d(depth) data = np.ma.atleast_2d(data).filled(np.mean(data)) lon = np.atleast_1d(lon) lat = np.atleast_1d(lat) # Generate the depths depth[depth > 0] *= -1 if z is None: z = np.linspace(depth.min() - 2, depth.max(), nz) else: z[z > 0] *= -1 nz = len(z) dz = np.abs(np.diff(z).mean()) # Determine the distance between points and the weighting to apply dist = np.hstack(([0], seapy.earth_distance( lon[0], lat[0], lon[1:], lat[1:]))) dx = np.diff(dist).mean() zscale = np.maximum(1, 10**int(np.log10(dx / dz))) dx /= zscale x = np.linspace(0, dist.max(), nx) # All arrays have to be the same size xx, zz = np.meshgrid(x / zscale, z) # For the source data, we don't want to extrpolate, # so make the data go from the surface to twice its # depth. zl = np.argsort(depth[:, 0]) dep = np.vstack((np.ones((1, depth.shape[1])) * 2 * depth.min(), depth[zl, :], np.zeros((1, depth.shape[1])))) # repeat the same data at the top and bottom dat = np.vstack((data[zl[0], :], data[zl], data[zl[-1], :])) dist = np.tile(dist.T, [dep.shape[0], 1]) / zscale # Find the bottom indices to create a mask for nodata/land idx = np.interp(xx[0, :], dist[0, :], np.interp(depth.min(axis=0), z, np.arange(nz))).astype(int) mask = np.arange(nz)[:, np.newaxis] <= idx # Interpolate ndat = np.ma.array(griddata( (dist.ravel(), dep.ravel()), dat.ravel(), (xx.ravel(), zz.ravel()), method='cubic').reshape(xx.shape), mask=mask) # Return everything return x, z, ndat