def ijk(self, points, depth_adjust=False): """ Compute the fractional i, j, k indices of the grid from a set of lon, lat, depth points. Parameters ---------- points : list of tuples, longitude, latitude, depth points to compute i, j, k indicies. NOTE: depth is in meters (defaults to negative) depth_adjust : bool, If True, depths that are deeper than the grid are set to the bottom layer, 0. If False, a nan value is used for values beyond the grid depth. Default is False. Returns ------- out : tuple of numpy.maskedarray (with netcdf-type indexing), list of k, j, i indices for the given lon, lat, depth points Examples -------- >>> a = ([-158, -160.5, -155.5], [20, 22.443, 19.5], [-10 -200 0]) >>> idx = g.ijk(a) """ # NOTE: Attempted to use a 3D griddata, but it took over 2 minutes # for each call, resulting in a 6minute runtime for this method # Reverted to 2D i,j indices, then looping a 1-D interpolation # to get depths for increased-speed (though this method is still slow) # Get the i,j points (j, i) = self.ij((points[0], points[1])) k = j * np.ma.masked grid_k = np.arange(0, self.n) depth = np.asanyarray(points[2]) depth[depth > 0] *= -1 # Determine the unique points good = np.where(~np.logical_or(i.mask, j.mask))[0] ii = np.floor(i[good]) jj = np.floor(j[good]) idx = seapy.unique_rows((jj, ii)) fill_value = 0 if depth_adjust else np.nan for n in idx: pts = np.where(np.logical_and(jj == jj[n], ii == ii[n])) fi = interp1d(self.depth_rho[:, jj[n], ii[n]], grid_k, bounds_error=False, fill_value=fill_value) k[good[pts]] = fi(depth[good][pts]) # Mask bad points l = np.isnan(k.data) i[l] = np.ma.masked j[l] = np.ma.masked k[l] = np.ma.masked return (k, j, i)
def add_ssh_tides(obs, tide_file, tide_error, tide_start=None, provenance=None, reftime=seapy.default_epoch): """ Apply predicted barotropic tides to the SSH values of given observations using the tide_file given. Parameters ---------- obs : seapy.roms.obs.obs or string, The observations to enforce the error profile upon. tide_file : string, The name of the ROMS tidal forcing file to use for predicting the barotropic tides. tide_error : np.masked_array A two dimensional array of the tidal fit errors to apply to the ssh errors when adding the tides. This should be the same size as the rho-grid. The units of the error must be in meters. If it is masked, the mask will be honored and obs that are in the mask will be removed. This allows you to filter on regions of high error. tide_start : bool, optional, If given, the tide_start of the tide file. If not specified, will read the attribute of the tidal forcing file provenance : list of int or string, optional, The provenance to apply the tides to (ignore other observations of the same type, but different instrument) reftime: datetime, Reference time for the observation times Returns ------- None: The obs structure is mutable is changed in place Examples -------- >>> obs = obs('observation_file.nc') >>> add_ssh_tides(obs, 'tide_frc.nc', errmap) The resulting 'obs' variable will have modified data. To save it: >>> obs.to_netcdf() """ # Load tidal file data frc = seapy.roms.tide.load_forcing(tide_file) if not tide_start: tide_start = frc['tide_start'] # Make sure that the sizes are the same if frc['Eamp'].shape[1:] != tide_error.shape: raise ValueError( "The error array is not the same size as the tidal grid") # Gather the observations that need tidal information obs = seapy.roms.obs.asobs(obs) pro = seapy.roms.obs.asprovenance(provenance) if provenance else None if pro: l = np.where(np.logical_and(obs.type == 1, np.in1d(obs.provenance, pro))) else: l = np.where(obs.type == 1) # If we have any, then do tidal predictions and add the signal # and error to the observations bad = [] if l[0].any(): ox = np.rint(obs.x[l]).astype(int) oy = np.rint(obs.y[l]).astype(int) idx = seapy.unique_rows((ox, oy)) for cur in seapy.progressbar.progress(idx): pts = np.where(np.logical_and(ox == ox[cur], oy == oy[cur])) # If this point is masked, remove from the observations if not tide_error[oy[cur], ox[cur]]: bad.append(l[0][pts].tolist()) else: time = [reftime + datetime.timedelta(t) for t in obs.time[l][pts]] amppha = seapy.tide.pack_amp_phase( frc['tides'], frc['Eamp'][:, oy[cur], ox[cur]], frc['Ephase'][:, oy[cur], ox[cur]]) zpred = seapy.tide.predict(time, amppha, lat=obs.lat[l][cur], tide_start=tide_start) # Add the information to the observations obs.value[l[0][pts]] += zpred obs.error[l[0][pts]] = np.maximum( obs.error[l[0][pts]], tide_error[oy[cur], ox[cur]]**2) # If any were bad, then remove them if bad: obs.delete(seapy.flatten(bad)) pass
def ijk(self, points, depth_adjust=False): """ Compute the fractional i, j, k indices of the grid from a set of lon, lat, depth points. Parameters ---------- points : list of tuples, longitude, latitude, depth points to compute i, j, k indicies. NOTE: depth is in meters (defaults to negative) depth_adjust : bool, If True, depths that are deeper (shallower) than the grid are set to the bottom (top) layer, 0 (N). If False, a nan value is used for values beyond the grid depth. Default is False. Returns ------- out : tuple of numpy.maskedarray (with netcdf-type indexing), list of k, j, i indices for the given lon, lat, depth points Examples -------- >>> a = ([-158, -160.5, -155.5], [20, 22.443, 19.5], [-10 -200 0]) >>> idx = g.ijk(a) """ # NOTE: Attempted to use a 3D griddata, but it took over 2 minutes # for each call, resulting in a 6minute runtime for this method # Reverted to 2D i,j indices, then looping a 1-D interpolation # to get depths for increased-speed (though this method is still slow) from scipy.interpolate import interp1d # Get the i,j points (j, i) = self.ij((points[0], points[1])) k = j * np.ma.masked grid_k = np.arange(0, self.n) depth = np.asanyarray(points[2]) depth[depth > 0] *= -1 # Determine the unique points good = np.where(~np.logical_or(i.mask, j.mask))[0] ii = np.floor(i[good]).astype(int) jj = np.floor(j[good]).astype(int) idx = seapy.unique_rows((jj, ii)) fill_value = 0 if depth_adjust else np.nan for n in idx: pts = np.where(np.logical_and(jj == jj[n], ii == ii[n])) griddep = self.depth_rho[:, jj[n], ii[n]] if griddep[0] < griddep[-1]: griddep[-1] = 0.0 else: griddep[0] = 0.0 fi = interp1d(griddep, grid_k, bounds_error=False, fill_value=fill_value) k[good[pts]] = fi(depth[good][pts]) # Mask bad points l = np.isnan(k.data) i[l] = np.ma.masked j[l] = np.ma.masked k[l] = np.ma.masked return (k, j, i)
def add_ssh_tides(obs, tide_file, tide_error, tide_start=None, provenance=None, reftime=seapy.default_epoch): """ Apply predicted barotropic tides to the SSH values of given observations using the tide_file given. Parameters ---------- obs : seapy.roms.obs.obs or string, The observations to enforce the error profile upon. tide_file : string, The name of the ROMS tidal forcing file to use for predicting the barotropic tides. tide_error : np.masked_array A two dimensional array of the tidal fit errors to apply to the ssh errors when adding the tides. This should be the same size as the rho-grid. The units of the error must be in meters. If it is masked, the mask will be honored and obs that are in the mask will be removed. This allows you to filter on regions of high error. tide_start : bool, optional, If given, the tide_start of the tide file. If not specified, will read the attribute of the tidal forcing file provenance : list of int or string, optional, The provenance to apply the tides to (ignore other observations of the same type, but different instrument) reftime: datetime, Reference time for the observation times Returns ------- None: The obs structure is mutable is changed in place Examples -------- >>> obs = obs('observation_file.nc') >>> add_ssh_tides(obs, 'tide_frc.nc', errmap) The resulting 'obs' variable will have modified data. To save it: >>> obs.to_netcdf() """ # Load tidal file data frc = seapy.roms.tide.load_forcing(tide_file) if not tide_start: tide_start = frc['tide_start'] # Make sure that the sizes are the same if frc['Eamp'].shape[1:] != tide_error.shape: raise ValueError( "The error array is not the same size as the tidal grid") # Gather the observations that need tidal information obs = seapy.roms.obs.asobs(obs) pro = seapy.roms.obs.asprovenance(provenance) if provenance else None if pro: l = np.where(np.logical_and(obs.type == 1, np.in1d(obs.provenance, pro))) else: l = np.where(obs.type == 1) # If we have any, then do tidal predictions and add the signal # and error to the observations bad = [] if l[0].any(): ox = np.rint(obs.x[l]).astype(int) oy = np.rint(obs.y[l]).astype(int) idx = seapy.unique_rows((ox, oy)) for cur in seapy.progressbar.progress(idx): pts = np.where(np.logical_and(ox == ox[cur], oy == oy[cur])) # If this point is masked, remove from the observations if not tide_error[oy[cur], ox[cur]]: bad.append(l[0][pts].tolist()) else: time = [reftime + datetime.timedelta(t) for t in obs.time[l][pts]] amppha = seapy.tide.pack_amp_phase( frc['tides'], frc['Eamp'][:, oy[cur], ox[cur]], frc['Ephase'][:, oy[cur], ox[cur]]) zpred = seapy.tide.predict(time, amppha, lat=obs.lat[l][cur], tide_start=tide_start) # Add the information to the observations obs.value[l[0][pts]] += zpred obs.error[l[0][pts]] = np.maximum( obs.error[l[0][pts]], tide_error[oy[cur], ox[cur]]**2) # If any were bad, then remove them if bad: obs.delete(seapy.flatten(bad)) pass