Example #1
0
    def apply_isopycnal_displacement(self, srho_file, rho_1_0=1031., method=1):
        """Input the path to picked array of smoothed potential density."""

        print("\nAdding isopycnal displacements.")
        print("-------------------------------\n")

        setattr(self, 'mu', np.nan*self.rho_1.copy())

        with open(srho_file) as f:

            srho_1 = _pickle.load(f)

        setattr(self, 'srho_1', srho_1)
        print("  Added: srho_1.")

        # Use smoothed profiles as they are.
        if method == 0:
            pass
        if method == 1:
            print("  Further smoothing by averaging adjecent profiles.")
            # Add initial smooth profiles to Profile objects.
            self.update_profiles()
            # Do more smoothing by averaging adjecent profiles.
            for i in xrange(len(self.hpid)):
                hpids = np.arange(self.hpid[i] - 5, self.hpid[i] + 6)
                self.srho_1[:, i] = self.get_mean_profile(hpids, 'srho_1',
                                                          self.z[:, i])

        else:
            raise ValueError('Invalid method.')

        for i in xrange(len(self.hpid)):
            srho_1dz = utils.finite_diff(self.z[:, i], srho_1[:, i],
                                         self.UTC[:, i])
            self.mu[:, i] = (self.rho_1[:, i] - srho_1[:, i])/srho_1dz

        print("  Added: mu.")

        # I added a minus because I *think* gravity should be negative.
        self.b = -gsw.grav(self.lat_start, self.P) \
             * (self.rho_1 - srho_1)/rho_1_0

        print("  Added: b.")

        self.update_profiles()
Example #2
0
def pgf(z, y, x, eta, rho, pa=0., rho0=1025., geographic=True):
	"""
	USAGE
	-----
	Py, Px = pgf(z, y, x, eta, rho, pa=0., rho0=1025., geographic=True)

	Calculates total horizontal pressure gradient force per unit mass [m/s2]
	(barotropic + baroclinic + barometric components), i.e.,

	P(z,y,x) = -(1/rho0)*grad_H(pa) -g*grad_H(eta) + (g/rho0)*Integral{grad_H(rho)}.

	'rho0' is the reference potential density used in the Boussinesq Approximation
	(defaults to 1025. kg/m3), 'g' is the gravitational acceleration, 'pa(y,x)' is
	the atmospheric pressure in [N/m2] (defults to 0.), 'eta(y,x)' is the free surface elevation in [m],
	'rho(z,y,x)' is the potential density and 'grad_H' is the horizontal gradient operator.
	The Integral(rho) is calculated from z' = eta(x,y) down through z' = z.

	The coordinate arrays (z,y,x) are distances in the (vertical,meridional,zonal)
	directions. The vertical axis originates at the surface (z = 0), i.e.,
	rho[0,y,x] = rho_surface and
	rho[-1,y,x] = rho_bottom.

	If geographic==True (default), (y,x) are assumed to be
	(latitude,longitude) and are converted to meters before
	computing (dy,dx). If geographic==False, (y,x) are assumed to be in meters.
	"""
	z, y, x, eta, rho = map(np.asanyarray, (z, y, x, eta, rho))

	ny, nx = eta.shape                       # Shape of the (x,y,u,v) arrays.
	if z.ndim==1:
		z = np.expand_dims(z, 1)
		z = np.expand_dims(z, 1)
		z = np.tile(z, (1, ny, nx))

	## Calculating grid spacings.
	if geographic:
		dlat, _ = np.gradient(y)
		_, dlon = np.gradient(x)
		deg2m = 111120.0                     # [m/deg]
		dx = dlon*deg2m*np.cos(y*np.pi/180.) # [m]
		dy = dlat*deg2m                      # [m]
	else:
		dy, _ = np.gradient(y)
		_, dx = np.gradient(x)

	dz, _, _ = np.gradient(z)
	dz = np.abs(dz)

	## Get gravitational acceleration.
	if geographic:
		g = grav(y)
	else:
		g = 9.81

	## pa (x,y) derivatives.
	if pa:
		dpay, dpax = np.gradient(pa)
		dpady = dpay/dy
		dpadx = dpax/dx

	## eta (x,y) derivatives.
	detay, detax = np.gradient(eta)
	detady = detay/dy
	detadx = detax/dx

	## rho (x,y) derivatives.
	_, drhoy, drhox = np.gradient(rho)
	drhody = drhoy/dy
	drhodx = drhox/dx

	## Barometric pressure gradient force per unit mass.
	if pa==0.:
		PGF_bm_y, PGF_bm_x = np.zeros((ny, nx)), np.zeros((ny, nx))
	else:
		PGF_bm_y = -dpady/rho0
		PGF_bm_x = -dpadx/rho0

	## Barotropic pressure gradient force per unit mass.
	PGF_bt_y = -g*detady
	PGF_bt_x = -g*detadx

	## Vertical integration from z' = eta(x,y) through z' = z.
	Iy = np.cumsum(drhody*dz, axis=0)
	Ix = np.cumsum(drhodx*dz, axis=0)

	## Baroclinic pressure gradient force per unit mass.
	PGF_bc_y = +g*Iy/rho0
	PGF_bc_x = +g*Ix/rho0

	## Total pressure gradient force per unit mass.
	PGF_y = PGF_bm_y + PGF_bt_y + PGF_bc_y
	PGF_x = PGF_bm_x + PGF_bt_x + PGF_bc_x

	return PGF_y, PGF_x
Example #3
0
    def __init__(self, h, T=None, L=None, thetao=None, Ho=None, lat=None):
        self.T = np.asarray(T, dtype=np.float)
        self.L = np.asarray(L, dtype=np.float)
        self.Ho = np.asarray(Ho, dtype=np.float)
        self.lat = np.asarray(lat, dtype=np.float)
        self.thetao = np.asarray(thetao, dtype=np.float)

        if isinstance(h, str):
            if L is not None:
                if h == 'deep':
                    self.h = self.L / 2.
                elif h == 'shallow':
                    self.h = self.L * 0.05
        else:
            self.h = np.asarray(h, dtype=np.float)

        if lat is None:
            g = 9.81  # Default gravity.
        else:
            g = gsw.grav(lat, p=0)

        if L is None:
            self.omega = 2 * np.pi / self.T
            self.Lo = (g * self.T ** 2) / 2 / np.pi
            # Returns wavenumber of the gravity wave dispersion relation using
            # newtons method. The initial guess is shallow water wavenumber.
            self.k = self.omega / np.sqrt(g)
            # TODO: May change to,
            # self.k = self.w ** 2 / (g * np.sqrt(self.w ** 2 * self.h / g))
            f = g * self.k * np.tanh(self.k * self.h) - self.omega ** 2

            while np.abs(f.max()) > 1e-10:
                dfdk = (g * self.k * self.h * (1 / (np.cosh(self.k * self.h)))
                        ** 2 + g * np.tanh(self.k * self.h))
                self.k = self.k - f / dfdk
                # FIXME:
                f = g * self.k * np.tanh(self.k * self.h) - self.omega ** 2

            self.L = 2 * np.pi / self.k
            if isinstance(h, str):
                if h == 'deep':
                    self.h = self.L / 2.
                elif h == 'shallow':
                    self.h = self.L * 0.05
        else:
            self.Lo = self.L / np.tanh(2 * np.pi * self.h / self.L)
            self.k = 2 * np.pi / self.L
            self.T = np.sqrt(2 * np.pi * self.Lo / g)
            self.omega = 2 * np.pi / self.T

        self.hoL = self.h / self.L
        self.hoLo = self.h / self.Lo
        self.C = self.omega / self.k  # or L / T
        self.Co = self.Lo / self.T
        self.G = 2 * self.k * self.h / np.sinh(2 * self.k * self.h)
        self.n = (1 + self.G) / 2
        self.Cg = self.n * self.C
        self.Ks = np.sqrt(1 / (1 + self.G) / np.tanh(self.k * self.h))

        if thetao is None:
            self.theta = np.NaN
            self.Kr = np.NaN
        if thetao is not None:
            self.theta = np.rad2deg(np.asin(self.C / self.Co *
                                            np.sin(np.deg2rad(self.thetao))))
            self.Kr = np.sqrt(np.cos(np.deg2rad(self.thetao)) /
                              np.cos(np.deg2rad(self.theta)))

        if Ho is None:
            self.H = np.NaN
        if Ho is not None:
            self.H = self.Ho * self.Ks * self.Kr
def adiabatic_level(P, SA, T, lat, P_bin_width=200., deg=1):
    """Generate smooth buoyancy frequency profile by applying the adiabatic
    levelling method of Bray and Fofonoff (1981).

    Parameters
    ----------
    P : 1-D ndarray
        Pressure [dbar]
    SA : 1-D ndarray
        Absolute salinity [g/kg]
    T : 1-D ndarray
        Temperature [degrees C]
    lat : float
        Latitude [-90...+90]
    p_bin_width : float, optional
        Pressure bin width [dbar]
    deg : int, optional
        Degree of polynomial fit. (DEGREES HIGHER THAN 1 NOT YET TESTED)

    Returns
    -------
    N2_ref : 1-D ndarray
        Reference buoyancy frequency [s-2]

    Notes
    -----
    Calls to the gibbs seawater toolbox are slow and therefore this function
    is quite slow.

    """

    N2_ref = np.NaN*P.copy()
    nans = np.isnan(P) | np.isnan(SA) | np.isnan(T)

    # If there are nothing but NaN values don't waste time.
    if np.sum(nans) == nans.size:
        return N2_ref

    P = P[~nans]
    SA = SA[~nans]
    T = T[~nans]
    P_min, P_max = np.min(P), np.max(P)

    shape = (P.size, P.size)
    Pm = np.NaN*np.empty(shape)
    SAm = np.NaN*np.empty(shape)
    Tm = np.NaN*np.empty(shape)

    # Populate bins.
    for i in xrange(len(P)):

        P_bin_min = np.maximum(P[i] - P_bin_width/2., P_min)
        P_bin_max = np.minimum(P[i] + P_bin_width/2., P_max)
        in_bin = np.where((P >= P_bin_min) & (P <= P_bin_max))[0]

        Pm[in_bin, i] = P[in_bin]
        SAm[in_bin, i] = SA[in_bin]
        Tm[in_bin, i] = T[in_bin]

    P_bar = np.nanmean(Pm, axis=0)
    T_bar = np.nanmean(Tm, axis=0)
    SA_bar = np.nanmean(SAm, axis=0)

    # Perform thermodynamics once only...
    rho_bar = gsw.pot_rho_t_exact(SA_bar, T_bar, P_bar, P_bar)
    sv = 1./gsw.pot_rho_t_exact(SAm, Tm, Pm, P_bar)

    p = []
    for P_bin, sv_bin in zip(Pm.T, sv.T):
        bnans = np.isnan(P_bin)
        p.append(np.polyfit(P_bin[~bnans],
                            sv_bin[~bnans] - np.nanmean(sv_bin),
                            deg))

    p = np.asarray(p)

    g = gsw.grav(lat, P_bar)
    # The factor 1e-4 is needed for conversion from dbar to Pa.
    N2_ref[~nans] = -1e-4*rho_bar**2*g**2*p[:, 0]

    return N2_ref
print("Estimating w and b.")
om = np.fft.fftfreq(nperseg, 15 * 60)
c4w["w_hi"] = np.fft.ifft(
    1j * pi2 * om[:, np.newaxis, np.newaxis] *
    np.fft.fft(-c4w["gamman_hi"] / c4["dgdz"], axis=0),
    axis=0,
).real
c4w["w_hib"] = np.fft.ifft(
    1j * pi2 * om[:, np.newaxis, np.newaxis] *
    np.fft.fft(-c4w["gamman_hib"] / c4["dgdz"], axis=0),
    axis=0,
).real

# Estimate buoyancy variables
c4w["b_hi"] = -gsw.grav(-c4["z"],
                        cc["lat"]) * c4w["gamman_hi"] / c4["gamman_lo"]
c4w["b_hib"] = -gsw.grav(-c4["z"],
                         cc["lat"]) * c4w["gamman_hib"] / c4["gamman_lo"]
c4["N"] = np.sqrt(c4["N2"])

print("Estimating covariance spectra.")
freq, c4w["Puu"] = sig.welch(c4w["u_hi"], **spec_kwargs)
_, c4w["Pvv"] = sig.welch(c4w["v_hi"], **spec_kwargs)
_, c4w["Pww"] = sig.welch(c4w["w_hi"], **spec_kwargs)
_, c4w["Pwwg"] = sig.welch(c4w["gamman_hi"] / c4["dgdz"], **spec_kwargs)
c4w["Pwwg"] *= (pi2 * freq[:, np.newaxis, np.newaxis])**2
_, c4w["Pbb"] = sig.welch(c4w["b_hi"], **spec_kwargs)
_, c4w["Cuv"] = sig.csd(c4w["u_hi"], c4w["v_hi"], **spec_kwargs)
_, c4w["Cuwg"] = sig.csd(c4w["u_hi"], c4w["gamman_hi"] / c4["dgdz"],
                         **spec_kwargs)
c4w["Cuwg"] *= -1j * pi2 * freq[:, np.newaxis, np.newaxis]
Example #6
0
def adiabatic_leveling(
    P,
    S,
    T,
    lon,
    lat,
    bin_width=100.0,
    order=1,
    return_diagnostics=False,
    cap=None,
):
    """Generate smooth buoyancy frequency profile by adiabatic leveling.

    Parameters
    ----------
    P : 1-D ndarray
        Pressure [dbar]
    S : 1-D ndarray
        Practical salinity [-]
    T : 1-D ndarray
        Temperature [degrees C]
    lon : float
        Longitude [-180...+360]
    lat : float
        Latitude [-90...+90]
    bin_width : float, optional
        Pressure bin width [dbar]
    order : int, optional
        Degree of polynomial fit.
    return_diagnostics : bool, optional
        Flag to return additional argument pcoefs. False by default.
    cap : {None, 'left', 'right', 'both'}, optional
        Flag to change proceedure at ends of array where bins may be partially
        filled. None by default, meaning they are included. Can also specify
        'left', 'right' or 'both' to cap method before partial bins.

    Returns
    -------
    N2_ref : 1-D ndarray
        Reference buoyancy frequency [s-2]
    pcoefs : 2-D ndarray
        Fitting coefficients, returned only when the flag return_diagnostics is set
        True.

    Notes
    -----
    Based on method of Bray and Fofonoff (1981) :cite:`Bray1981`.

    """
    valid = np.isfinite(P) & np.isfinite(S) & np.isfinite(T)
    valid = np.squeeze(np.argwhere(valid))
    P_, S_, T_ = P[valid], S[valid], T[valid]

    flip = False
    if (np.diff(P_) < 0).all():
        flip = True
        P_ = np.flipud(P_)
        S_ = np.flipud(S_)
        T_ = np.flipud(T_)
    elif (np.diff(P_) < 0).any():
        raise ValueError("P must be monotonically increasing/decreasing.")

    i1 = np.searchsorted(P_, P_ - bin_width / 2.0)
    i2 = np.searchsorted(P_, P_ + bin_width / 2.0)

    if cap is None:
        Nd = P_.size
    elif cap == "both":
        icapl = i2[0]
        icapr = i1[-1]
    elif cap == "left":
        icapl = i2[0]
        icapr = i2[-1]
    elif cap == "right":
        icapl = i1[0]
        icapr = i1[-1]
    else:
        raise ValueError("The argument cap must be either None, 'both', 'left'"
                         " or 'right'")

    if cap is not None:
        i1 = i1[icapl:icapr]
        i2 = i2[icapl:icapr]
        valid = valid[icapl:icapr]
        Nd = icapr - icapl

    dimax = np.max(i2 - i1)

    Pb = np.full((dimax, Nd), np.nan)
    Sb = np.full((dimax, Nd), np.nan)
    Tb = np.full((dimax, Nd), np.nan)

    for i in range(Nd):
        imax = i2[i] - i1[i]
        Pb[:imax, i] = P_[i1[i]:i2[i]]
        Sb[:imax, i] = S_[i1[i]:i2[i]]
        Tb[:imax, i] = T_[i1[i]:i2[i]]

    Pbar = np.nanmean(Pb, axis=0)

    SAb = gsw.SA_from_SP(Sb, Pb, lon, lat)

    rho = gsw.pot_rho_t_exact(SAb, Tb, Pb, Pbar)
    sv = 1.0 / rho

    rhobar = np.nanmean(rho, axis=0)

    p = np.full((order + 1, Nd), np.nan)

    for i in range(Nd):
        imax = i2[i] - i1[i]
        p[:, i] = np.polyfit(Pb[:imax, i], sv[:imax, i], order)

    g = gsw.grav(lat, Pbar)
    # The factor 1e-4 is needed for conversion from dbar to Pa.
    if order == 1:
        N2 = -1e-4 * rhobar**2 * g**2 * p[0, :]
    elif order == 2:
        N2 = -1e-4 * rhobar**2 * g**2 * (p[1, :] + 2 * Pbar * p[0, :])
    elif order == 3:
        N2 = (-1e-4 * rhobar**2 * g**2 *
              (p[2, :] + 2 * Pbar * p[1, :] + 3 * Pbar**2 * p[0, :]))
    else:
        raise ValueError("Fits are only included up to 3rd order.")

    N2_ref = np.full_like(P, np.nan)
    pcoef = np.full((order + 1, P.size), np.nan)
    if flip:
        N2_ref[valid] = np.flipud(N2)
        pcoef[:, valid] = np.fliplr(p)
    else:
        N2_ref[valid] = N2
        pcoef[:, valid] = p

    if return_diagnostics:
        return N2_ref, pcoef
    else:
        return N2_ref
Example #7
0
    def __init__(self, h, T=None, L=None, thetao=None, Ho=None, lat=None):
        self.T = np.asarray(T, dtype=np.float)
        self.L = np.asarray(L, dtype=np.float)
        self.Ho = np.asarray(Ho, dtype=np.float)
        self.lat = np.asarray(lat, dtype=np.float)
        self.thetao = np.asarray(thetao, dtype=np.float)

        if isinstance(h, str):
            if L is not None:
                if h == "deep":
                    self.h = self.L / 2.0
                elif h == "shallow":
                    self.h = self.L * 0.05
        else:
            self.h = np.asarray(h, dtype=np.float)

        if lat is None:
            g = 9.81  # Default gravity.
        else:
            g = gsw.grav(lat, p=0)

        if L is None:
            self.omega = 2 * np.pi / self.T
            self.Lo = (g * self.T**2) / 2 / np.pi
            # Returns wavenumber of the gravity wave dispersion relation using
            # newtons method. The initial guess is shallow water wavenumber.
            self.k = self.omega / np.sqrt(g)
            # TODO: May change to,
            # self.k = self.w ** 2 / (g * np.sqrt(self.w ** 2 * self.h / g))
            f = g * self.k * np.tanh(self.k * self.h) - self.omega**2

            while np.abs(f.max()) > 1e-10:
                dfdk = g * self.k * self.h * (1 / (np.cosh(
                    self.k * self.h)))**2 + g * np.tanh(self.k * self.h)
                self.k = self.k - f / dfdk
                # FIXME:
                f = g * self.k * np.tanh(self.k * self.h) - self.omega**2

            self.L = 2 * np.pi / self.k
            if isinstance(h, str):
                if h == "deep":
                    self.h = self.L / 2.0
                elif h == "shallow":
                    self.h = self.L * 0.05
        else:
            self.Lo = self.L / np.tanh(2 * np.pi * self.h / self.L)
            self.k = 2 * np.pi / self.L
            self.T = np.sqrt(2 * np.pi * self.Lo / g)
            self.omega = 2 * np.pi / self.T

        self.hoL = self.h / self.L
        self.hoLo = self.h / self.Lo
        self.C = self.omega / self.k  # or L / T
        self.Co = self.Lo / self.T
        self.G = 2 * self.k * self.h / np.sinh(2 * self.k * self.h)
        self.n = (1 + self.G) / 2
        self.Cg = self.n * self.C
        self.Ks = np.sqrt(1 / (1 + self.G) / np.tanh(self.k * self.h))

        if thetao is None:
            self.theta = np.NaN
            self.Kr = np.NaN
        if thetao is not None:
            self.theta = np.rad2deg(
                np.asin(self.C / self.Co * np.sin(np.deg2rad(self.thetao))))
            self.Kr = np.sqrt(
                np.cos(np.deg2rad(self.thetao)) /
                np.cos(np.deg2rad(self.theta)))

        if Ho is None:
            self.H = np.NaN
        if Ho is not None:
            self.H = self.Ho * self.Ks * self.Kr
Example #8
0
                                                 dict_vars['CT'])

            # save dictionary to file
            write_dict(dict_vars, path, filename)
            print('data stored in file')

        elif filename[4:] == 'stations':
            for station in dict_ctd['station'][0, :]:
                dict_stations[station]['SA'] = SA_from_SP(
                    dict_stations[station]['S'], dict_stations['P'],
                    dict_stations[station]['lon'],
                    dict_stations[station]['lat'])
                dict_stations[station]['CT'] = CT_from_t(
                    dict_stations[station]['SA'], dict_stations[station]['T'],
                    dict_stations['P'])
                dict_stations[station]['g'] = grav(
                    dict_stations[station]['lat'], dict_stations['P'])
                dict_stations[station]['depth'] = abs(
                    z_from_p(dict_stations['P'],
                             dict_stations[station]['lat']))
                dict_stations[station]['pt'] = pt_from_t(
                    dict_stations[station]['SA'], dict_stations[station]['T'],
                    dict_stations['P'], p_ref)
                dict_stations[station]['sigma0'] = sigma0(
                    dict_stations[station]['SA'], dict_stations[station]['CT'])
                dict_stations[station]['spiciness0'] = spiciness0(
                    dict_stations[station]['SA'], dict_stations[station]['CT'])
                dict_stations[station]['deltaD'] = geo_strf_dyn_height(
                    dict_stations[station]['SA'],
                    dict_stations[station]['CT'],
                    dict_stations['P'],
                    p_ref=p_ref)