Example #1
0
def correct_ts(datadir, filename, corr_coeff_filename):
    """
    Corrects TB Temp and salinity according to correlation coefficients and adds corrected 
    var to ncfile
    Also converts practical salinity to absolute salinity and conservative temperature to 
    potential temperature
    """
    coeffs = pd.read_csv(corr_coeff_filename, delim_whitespace=True)
    ls = os.listdir(datadir)
    ls.sort()
    for filename in ls:
        if filename[:11] == 'TB_20181211':
            # print(filename)
            data = xr.open_dataset(os.path.join(datadir, filename))
            varkey = [i for i in data.data_vars.keys()]
            if 'lon' in varkey:
                print(filename)
                data['t_corrected']=('DEPTH', \
                           (coeffs.a_temp.values*data.TEMP+(coeffs.b_temp.values)))
                data['s_corrected']=('DEPTH', \
                           (coeffs.a_sal.values*data.PSAL+(coeffs.b_sal.values)))
                data['ab_sal']=('DEPTH', \
                           gsw.SA_from_SP(data.s_corrected,data.DEPTH,data.lon.values,data.lat.values))
                data['ptemp']=('DEPTH', \
                           gsw.pt_from_CT(data.ab_sal,data.TEMP))
                data['ab_sal_bal']=('DEPTH', \
                           gsw.SA_from_SP_Baltic(data.s_corrected,data.lon.values,data.lat.values))
                data['ptemp_bal']=('DEPTH', \
                           gsw.pt_from_CT(data.ab_sal_bal,data.TEMP))

                data.to_netcdf(
                    os.path.join('Data/ctd_files/gridded_calibrated_updated',
                                 filename))
Example #2
0
def oned_moxy(tsal, ttemp, tdic, tta, pres_atm, depth_this):

    ### GET pCO2 (and Omega, etc) GIVEN DIC, TA
    import sys
    sys.path.append('/data/tjarniko/mocsy')
    import mocsy
    import numpy as np
    import gsw

    size_box = np.shape(tdic)
    size_0 = size_box[0]
    size_1 = size_box[1]

    tsra = np.ravel(tsal)
    ttera = np.ravel(ttemp)
    #convert cons. temperature to potential temperature
    ttera_pot = gsw.pt_from_CT(tsra, ttera)

    ttara = np.ravel(tta) * 1e-3
    tdra = np.ravel(tdic) * 1e-3
    tzero = np.zeros_like(tsra)
    tpressure = np.zeros_like(tsra)
    #tdepth = np.zeros_like(tsra)
    tpressure[:] = pres_atm
    tdepth = np.ravel(depth_this)
    tzero = tpressure * 0

    tsra_psu = tsra * 35 / 35.16504
    #ttera_is = gsw.t_from_CT(tsra,ttera,tzero)

    response_tup = mocsy.mvars(temp=ttera_pot,
                               sal=tsra_psu,
                               alk=ttara,
                               dic=tdra,
                               sil=tzero,
                               phos=tzero,
                               patm=tpressure,
                               depth=tdepth,
                               lat=tzero,
                               optcon='mol/m3',
                               optt='Tpot',
                               optp='m',
                               optb='l10',
                               optk1k2='m10',
                               optkf='dg',
                               optgas='Pinsitu')
    pH, pco2, fco2, co2, hco3, co3, OmegaA, OmegaC, BetaD, DENis, p, Tis = response_tup

    pHr = pH.reshape(size_0, size_1)
    OmAr = OmegaA.reshape(size_0, size_1)
    pco2r = pco2.reshape(size_0, size_1)

    return pHr, OmAr, pco2r
Example #3
0
def convert_ts(datadir, filename):
    """
    Function to convert Practical salinity to Absolute salinity 
    and Conservative Temperature to potential temperature
    """
    ls = os.listdir(datadir)
    ls.sort()
    for filename in ls:
        if filename[:11] == 'SK_20181210':
            # print(filename)
            data = xr.open_dataset(os.path.join(datadir, filename))
            print(filename)
            data['ab_sal']=('DEPTH', \
                            gsw.SA_from_SP(data.PSAL,data.DEPTH,data.lon.values,data.lat.values))
            data['ptemp']=('DEPTH', \
                           gsw.pt_from_CT(data.ab_sal,data.TEMP))
            data['ab_sal_bal']=('DEPTH', \
                                gsw.SA_from_SP_Baltic(data.PSAL,data.lon.values,data.lat.values))
            data['ptemp_bal']=('DEPTH', \
                           gsw.pt_from_CT(data.ab_sal_bal,data.TEMP))

            data.to_netcdf(
                os.path.join('Data/ctd_files/gridded_calibrated_updated',
                             filename))
Example #4
0
def point_moxy(tsal, ttemp, tdic, tta, pres_atm, depth_this):

    ### GET pCO2 (and Omega, etc) GIVEN DIC, TA
    import sys
    sys.path.append('/data/tjarniko/mocsy')
    import mocsy
    import numpy as np
    import gsw

    tsra = (tsal)
    ttera = (ttemp)
    #convert cons. temperature to potential temperature
    ttera_pot = gsw.pt_from_CT(tsra, ttera)

    ttara = (tta) * 1e-3
    tdra = (tdic) * 1e-3
    tzero = 0
    tpressure = pres_atm
    tdepth = (depth_this)
    tsra_psu = tsra * 35 / 35.16504
    #ttera_is = gsw.t_from_CT(tsra,ttera,tzero)

    response_tup = mocsy.mvars(temp=ttera_pot,
                               sal=tsra_psu,
                               alk=ttara,
                               dic=tdra,
                               sil=tzero,
                               phos=tzero,
                               patm=tpressure,
                               depth=tdepth,
                               lat=tzero,
                               optcon='mol/m3',
                               optt='Tpot',
                               optp='m',
                               optb='l10',
                               optk1k2='m10',
                               optkf='dg',
                               optgas='Pinsitu')
    pH, pco2, fco2, co2, hco3, co3, OmegaA, OmegaC, BetaD, DENis, p, Tis = response_tup

    pHr = pH
    OmAr = OmegaA
    pco2r = pco2

    return pHr, OmAr, pco2r
def text_to_netcdf(inDir, outDir):
    inFileName = '{}/Antarctic_shelf_data.txt'.format(inDir)
    outFileName = '{}/Schmidtko_et_al_2014_bottom_PT_S_PD_' \
                  'SouthernOcean_0.25x0.125degree.nc'.format(outDir)

    if os.path.exists(outFileName):
        return

    # 1/4 x 1/8 degree grid cells
    cellsPerLon = 4
    cellsPerLat = 8

    obsFile = pandas.read_csv(inFileName, delim_whitespace=True)

    inLon = numpy.array(obsFile.iloc[:, 0])
    inLat = numpy.array(obsFile.iloc[:, 1])

    inZ = numpy.array(obsFile.iloc[:, 2])

    inCT = numpy.array(obsFile.iloc[:, 3])
    inCT_std = numpy.array(obsFile.iloc[:, 4])

    inSA = numpy.array(obsFile.iloc[:, 5])
    inSA_std = numpy.array(obsFile.iloc[:, 6])

    pressure = gsw.p_from_z(inZ, inLat)
    inS = gsw.SP_from_SA(inSA, pressure, inLon, inLat)
    inPT = gsw.pt_from_CT(inSA, inCT)
    inPD = gsw.rho(inSA, inCT, 0.)

    minLat = int(numpy.amin(inLat) * cellsPerLat) / cellsPerLat
    maxLat = int(numpy.amax(inLat) * cellsPerLat) / cellsPerLat
    deltaLat = 1. / cellsPerLat
    outLat = numpy.arange(minLat - deltaLat, maxLat + 2 * deltaLat, deltaLat)

    deltaLon = 1. / cellsPerLon
    outLon = numpy.arange(0., 360., deltaLon)

    xIndices = numpy.array(cellsPerLon * inLon + 0.5, int)
    yIndices = numpy.array(cellsPerLat * (inLat - outLat[0]) + 0.5, int)

    Lon, Lat = numpy.meshgrid(outLon, outLat)

    ds = xarray.Dataset()
    ds['lon'] = (('lon', ), outLon)
    ds.lon.attrs['units'] = 'degrees'
    ds.lon.attrs['description'] = 'longitutude'

    ds['lat'] = (('lat', ), outLat)
    ds.lat.attrs['units'] = 'degrees'
    ds.lat.attrs['description'] = 'latitutude'

    z = numpy.ma.masked_all(Lon.shape)
    z[yIndices, xIndices] = inZ
    ds['z'] = (('lat', 'lon'), z)
    ds.z.attrs['units'] = 'meters'
    ds.z.attrs['description'] = 'depth of the seafloor (positive up)'

    PT = numpy.ma.masked_all(Lon.shape)
    PT[yIndices, xIndices] = inPT
    ds['botTheta'] = (('lat', 'lon'), PT)
    ds.botTheta.attrs['units'] = '$\degree$C'
    ds.botTheta.attrs['description'] = \
        'potential temperature at sea floor'

    PT_std = numpy.ma.masked_all(Lon.shape)
    # neglect difference between std of PT and CT
    PT_std[yIndices, xIndices] = inCT_std
    ds['botThetaStd'] = (('lat', 'lon'), PT_std)
    ds.botThetaStd.attrs['units'] = '$\degree$C'
    ds.botThetaStd.attrs['description'] = \
        'standard deviation in potential temperature at sea floor'

    S = numpy.ma.masked_all(Lon.shape)
    S[yIndices, xIndices] = inS
    ds['botSalinity'] = (('lat', 'lon'), S)
    ds.botSalinity.attrs['units'] = 'PSU'
    ds.botSalinity.attrs['description'] = \
        'salinity at sea floor'

    S_std = numpy.ma.masked_all(Lon.shape)
    # neglect difference between std of S and SA
    S_std[yIndices, xIndices] = inSA_std
    ds['botSalinityStd'] = (('lat', 'lon'), S_std)
    ds.botSalinityStd.attrs['units'] = 'PSU'
    ds.botSalinityStd.attrs['description'] = \
        'standard deviation in salinity at sea floor'

    PD = numpy.ma.masked_all(Lon.shape)
    PD[yIndices, xIndices] = inPD
    ds['botPotentialDensity'] = (('lat', 'lon'), PD)
    ds.botPotentialDensity.attrs['units'] = 'kg m$^{-3}$'
    ds.botPotentialDensity.attrs['description'] = \
        'potential desnity at sea floor'

    write_netcdf(ds, outFileName)
Example #6
0
def find_DIC_corresp_to_pco2(tsal, ttemp, tpco2, tta, pres_atm, depth_this):

    import numpy as np
    import mocsy
    import gsw

    steps = 10000
    tsal_r = np.zeros([steps])
    tsal_r[:] = tsal
    #convert to psu
    tsal_r_psu = tsal_r * 35 / 35.16504

    ttemp_r = np.zeros([steps])
    ttemp_r[:] = ttemp
    #convert temperature to potential temperature
    ttemp_r_pot = gsw.pt_from_CT(tsal_r, ttemp_r)
    tta_r = np.zeros([steps])
    tta_r[:] = tta * 1e-3
    tpres_r = np.zeros([steps])
    tpres_r[:] = pres_atm
    depth_r = np.zeros([steps])
    depth_r[:] = depth_this
    tzero = np.zeros([steps])
    tlat = np.zeros([steps])
    tlat[:] = 50

    end_d = 2400
    start_d = 600
    intvl = (end_d - start_d) / steps
    tdic_r = np.arange(start_d, end_d - 0.1, intvl) * 1e-3
    #change to take potential temperature
    response_tup = mocsy.mvars(temp=ttemp_r_pot,
                               sal=tsal_r_psu,
                               alk=tta_r,
                               dic=tdic_r,
                               sil=tzero,
                               phos=tzero,
                               patm=tpres_r,
                               depth=depth_r,
                               lat=tzero,
                               optcon='mol/m3',
                               optt='Tpot',
                               optp='m',
                               optb='l10',
                               optk1k2='m10',
                               optkf='dg',
                               optgas='Pinsitu')
    pH, pco2, fco2, co2, hco3, co3, OmegaA, OmegaC, BetaD, DENis, p, Tis = response_tup

    diffmat = pco2 - tpco2
    idx, ans = find_nearest(diffmat, 0)

    if ans > 2:
        print('Danger, pco2 found >2 uatm from pco2 given')


#     print(idx)
#     print('difference between real pco2 and pco2 from calc. dic: ',ans)
#     print('DIC found this way:', tdic_r[idx]*1e3)
    fin_dic = tdic_r[idx] * 1e3

    return fin_dic
Example #7
0
    def post_process(self, verbose=True):

        print("\nPost processing")
        print("---------------\n")

        # Very basic
        self.ascent = self.hpid % 2 == 0
        self.ascent_ctd = self.ascent * np.ones_like(self.UTC, dtype=int)
        self.ascent_ef = self.ascent * np.ones_like(self.UTCef, dtype=int)

        # Estimate number of observations.
        self.nobs_ctd = np.sum(~np.isnan(self.UTC), axis=0)
        self.nobs_ef = np.sum(~np.isnan(self.UTCef), axis=0)

        # Figure out some useful times.
        self.UTC_start = self.UTC[0, :]
        self.UTC_end = np.nanmax(self.UTC, axis=0)

        if verbose:
            print("Creating time variable dUTC with units of seconds.")
        self.dUTC = (self.UTC - self.UTC_start) * 86400
        self.dUTCef = (self.UTCef - self.UTC_start) * 86400

        if verbose:
            print("Interpolated GPS positions to starts and ends of profiles.")
        # GPS interpolation to the start and end time of each half profile.
        idxs = ~np.isnan(self.lon_gps) & ~np.isnan(self.lat_gps)
        self.lon_start = np.interp(self.UTC_start, self.utc_gps[idxs],
                                   self.lon_gps[idxs])
        self.lat_start = np.interp(self.UTC_start, self.utc_gps[idxs],
                                   self.lat_gps[idxs])
        self.lon_end = np.interp(self.UTC_end, self.utc_gps[idxs],
                                 self.lon_gps[idxs])
        self.lat_end = np.interp(self.UTC_end, self.utc_gps[idxs],
                                 self.lat_gps[idxs])

        if verbose:
            print("Calculating heights.")
        # Depth.
        self.z = gsw.z_from_p(self.P, self.lat_start)
        #        self.z_ca = gsw.z_from_p(self.P_ca, self.lat_start)
        self.zef = gsw.z_from_p(self.Pef, self.lat_start)

        if verbose:
            print("Calculating distance along trajectory.")
        # Distance along track from first half profile.
        self.__ddist = utils.lldist(self.lon_start, self.lat_start)
        self.dist = np.hstack((0., np.cumsum(self.__ddist)))

        if verbose:
            print("Interpolating distance to measurements.")
        # Distances, velocities and speeds of each half profile.
        self.profile_ddist = np.zeros_like(self.lon_start)
        self.profile_dt = np.zeros_like(self.lon_start)
        self.profile_bearing = np.zeros_like(self.lon_start)
        lons = np.zeros((len(self.lon_start), 2))
        lats = lons.copy()
        times = lons.copy()

        lons[:, 0], lons[:, 1] = self.lon_start, self.lon_end
        lats[:, 0], lats[:, 1] = self.lat_start, self.lat_end
        times[:, 0], times[:, 1] = self.UTC_start, self.UTC_end

        self.dist_ctd = self.UTC.copy()
        nans = np.isnan(self.dist_ctd)
        for i, (lon, lat, time) in enumerate(zip(lons, lats, times)):
            self.profile_ddist[i] = utils.lldist(lon, lat)
            # Convert time from days to seconds.
            self.profile_dt[i] = np.diff(time) * 86400.

            d = np.array([self.dist[i], self.dist[i] + self.profile_ddist[i]])
            idxs = ~nans[:, i]
            self.dist_ctd[idxs, i] = np.interp(self.UTC[idxs, i], time, d)

        self.dist_ef = self.__regrid('ctd', 'ef', self.dist_ctd)

        if verbose:
            print("Estimating bearings.")
        # Pythagorian approximation (?) of bearing.
        self.profile_bearing = np.arctan2(self.lon_end - self.lon_start,
                                          self.lat_end - self.lat_start)

        if verbose:
            print("Calculating sub-surface velocity.")
        # Convert to m s-1 calculate meridional and zonal velocities.
        self.sub_surf_speed = self.profile_ddist * 1000. / self.profile_dt
        self.sub_surf_u = self.sub_surf_speed * np.sin(self.profile_bearing)
        self.sub_surf_v = self.sub_surf_speed * np.cos(self.profile_bearing)

        if verbose:
            print("Interpolating missing velocity values.")
        # Fill missing U, V values using linear interpolation otherwise we
        # run into difficulties using cumtrapz next.
        self.U = self.__fill_missing(self.U)
        self.V = self.__fill_missing(self.V)

        # Absolute velocity
        self.calculate_absolute_velocity(verbose=verbose)

        if verbose:
            print("Calculating thermodynamic variables.")
        # Derive some important thermodynamics variables.

        # Absolute salinity.
        self.SA = gsw.SA_from_SP(self.S, self.P, self.lon_start,
                                 self.lat_start)
        # Conservative temperature.
        self.CT = gsw.CT_from_t(self.SA, self.T, self.P)

        # Potential temperature with respect to 0 dbar.
        self.PT = gsw.pt_from_CT(self.SA, self.CT)

        # In-situ density.
        self.rho = gsw.rho(self.SA, self.CT, self.P)

        # Potential density with respect to 1000 dbar.
        self.rho_1 = gsw.pot_rho_t_exact(self.SA, self.T, self.P, p_ref=1000.)

        # Buoyancy frequency regridded onto ctd grid.
        N2_ca, __ = gsw.Nsquared(self.SA, self.CT, self.P, self.lat_start)
        self.N2 = self.__regrid('ctd_ca', 'ctd', N2_ca)

        if verbose:
            print("Calculating float vertical velocity.")
        # Vertical velocity regridded onto ctd grid.
        dt = 86400. * np.diff(self.UTC, axis=0)  # [s]
        Wz_ca = np.diff(self.z, axis=0) / dt
        self.Wz = self.__regrid('ctd_ca', 'ctd', Wz_ca)

        if verbose:
            print("Renaming Wp to Wpef.")
        # Vertical water velocity.
        self.Wpef = self.Wp.copy()
        del self.Wp

        if verbose:
            print("Calculating shear.")
        # Shear calculations.
        dUdz_ca = np.diff(self.U, axis=0) / np.diff(self.zef, axis=0)
        dVdz_ca = np.diff(self.V, axis=0) / np.diff(self.zef, axis=0)
        self.dUdz = self.__regrid('ef_ca', 'ef', dUdz_ca)
        self.dVdz = self.__regrid('ef_ca', 'ef', dVdz_ca)

        if verbose:
            print("Calculating Richardson number.")
        N2ef = self.__regrid('ctd', 'ef', self.N2)
        self.Ri = N2ef / (self.dUdz**2 + self.dVdz**2)

        if verbose:
            print("Regridding piston position to ctd.\n")
        # Regrid piston position.
        self.ppos = self.__regrid('ctd_ca', 'ctd', self.ppos_ca)

        self.update_profiles()
Example #8
0
    def post_process(self, verbose=True):

        print("\nPost processing")
        print("---------------\n")

        # Very basic
        self.ascent = self.hpid % 2 == 0
        self.ascent_ctd = self.ascent*np.ones_like(self.UTC, dtype=int)
        self.ascent_ef = self.ascent*np.ones_like(self.UTCef, dtype=int)

        # Estimate number of observations.
        self.nobs_ctd = np.sum(~np.isnan(self.UTC), axis=0)
        self.nobs_ef = np.sum(~np.isnan(self.UTCef), axis=0)

        # Figure out some useful times.
        self.UTC_start = self.UTC[0, :]
        self.UTC_end = np.nanmax(self.UTC, axis=0)

        if verbose:
            print("Creating time variable dUTC with units of seconds.")
        self.dUTC = (self.UTC - self.UTC_start)*86400
        self.dUTCef = (self.UTCef - self.UTC_start)*86400

        if verbose:
            print("Interpolated GPS positions to starts and ends of profiles.")
        # GPS interpolation to the start and end time of each half profile.
        idxs = ~np.isnan(self.lon_gps) & ~np.isnan(self.lat_gps)
        self.lon_start = np.interp(self.UTC_start, self.utc_gps[idxs],
                                   self.lon_gps[idxs])
        self.lat_start = np.interp(self.UTC_start, self.utc_gps[idxs],
                                   self.lat_gps[idxs])
        self.lon_end = np.interp(self.UTC_end, self.utc_gps[idxs],
                                 self.lon_gps[idxs])
        self.lat_end = np.interp(self.UTC_end, self.utc_gps[idxs],
                                 self.lat_gps[idxs])

        if verbose:
            print("Calculating heights.")
        # Depth.
        self.z = gsw.z_from_p(self.P, self.lat_start)
#        self.z_ca = gsw.z_from_p(self.P_ca, self.lat_start)
        self.zef = gsw.z_from_p(self.Pef, self.lat_start)

        if verbose:
            print("Calculating distance along trajectory.")
        # Distance along track from first half profile.
        self.__ddist = utils.lldist(self.lon_start, self.lat_start)
        self.dist = np.hstack((0., np.cumsum(self.__ddist)))

        if verbose:
            print("Interpolating distance to measurements.")
        # Distances, velocities and speeds of each half profile.
        self.profile_ddist = np.zeros_like(self.lon_start)
        self.profile_dt = np.zeros_like(self.lon_start)
        self.profile_bearing = np.zeros_like(self.lon_start)
        lons = np.zeros((len(self.lon_start), 2))
        lats = lons.copy()
        times = lons.copy()

        lons[:, 0], lons[:, 1] = self.lon_start, self.lon_end
        lats[:, 0], lats[:, 1] = self.lat_start, self.lat_end
        times[:, 0], times[:, 1] = self.UTC_start, self.UTC_end

        self.dist_ctd = self.UTC.copy()
        nans = np.isnan(self.dist_ctd)
        for i, (lon, lat, time) in enumerate(zip(lons, lats, times)):
            self.profile_ddist[i] = utils.lldist(lon, lat)
            # Convert time from days to seconds.
            self.profile_dt[i] = np.diff(time)*86400.

            d = np.array([self.dist[i], self.dist[i] + self.profile_ddist[i]])
            idxs = ~nans[:, i]
            self.dist_ctd[idxs, i] = np.interp(self.UTC[idxs, i], time, d)

        self.dist_ef = self.__regrid('ctd', 'ef', self.dist_ctd)

        if verbose:
            print("Estimating bearings.")
        # Pythagorian approximation (?) of bearing.
        self.profile_bearing = np.arctan2(self.lon_end - self.lon_start,
                                          self.lat_end - self.lat_start)

        if verbose:
            print("Calculating sub-surface velocity.")
        # Convert to m s-1 calculate meridional and zonal velocities.
        self.sub_surf_speed = self.profile_ddist*1000./self.profile_dt
        self.sub_surf_u = self.sub_surf_speed*np.sin(self.profile_bearing)
        self.sub_surf_v = self.sub_surf_speed*np.cos(self.profile_bearing)

        if verbose:
            print("Interpolating missing velocity values.")
        # Fill missing U, V values using linear interpolation otherwise we
        # run into difficulties using cumtrapz next.
        self.U = self.__fill_missing(self.U)
        self.V = self.__fill_missing(self.V)

        # Absolute velocity
        self.calculate_absolute_velocity(verbose=verbose)

        if verbose:
            print("Calculating thermodynamic variables.")
        # Derive some important thermodynamics variables.

        # Absolute salinity.
        self.SA = gsw.SA_from_SP(self.S, self.P, self.lon_start,
                                 self.lat_start)
        # Conservative temperature.
        self.CT = gsw.CT_from_t(self.SA, self.T, self.P)

        # Potential temperature with respect to 0 dbar.
        self.PT = gsw.pt_from_CT(self.SA, self.CT)

        # In-situ density.
        self.rho = gsw.rho(self.SA, self.CT, self.P)

        # Potential density with respect to 1000 dbar.
        self.rho_1 = gsw.pot_rho_t_exact(self.SA, self.T, self.P, p_ref=1000.)

        # Buoyancy frequency regridded onto ctd grid.
        N2_ca, __ = gsw.Nsquared(self.SA, self.CT, self.P, self.lat_start)
        self.N2 = self.__regrid('ctd_ca', 'ctd', N2_ca)

        if verbose:
            print("Calculating float vertical velocity.")
        # Vertical velocity regridded onto ctd grid.
        dt = 86400.*np.diff(self.UTC, axis=0)  # [s]
        Wz_ca = np.diff(self.z, axis=0)/dt
        self.Wz = self.__regrid('ctd_ca', 'ctd', Wz_ca)

        if verbose:
            print("Renaming Wp to Wpef.")
        # Vertical water velocity.
        self.Wpef = self.Wp.copy()
        del self.Wp

        if verbose:
            print("Calculating shear.")
        # Shear calculations.
        dUdz_ca = np.diff(self.U, axis=0)/np.diff(self.zef, axis=0)
        dVdz_ca = np.diff(self.V, axis=0)/np.diff(self.zef, axis=0)
        self.dUdz = self.__regrid('ef_ca', 'ef', dUdz_ca)
        self.dVdz = self.__regrid('ef_ca', 'ef', dVdz_ca)

        if verbose:
            print("Calculating Richardson number.")
        N2ef = self.__regrid('ctd', 'ef', self.N2)
        self.Ri = N2ef/(self.dUdz**2 + self.dVdz**2)

        if verbose:
            print("Regridding piston position to ctd.\n")
        # Regrid piston position.
        self.ppos = self.__regrid('ctd_ca', 'ctd', self.ppos_ca)

        self.update_profiles()
Example #9
0
    def teos10(self, vlist: list = ['SA', 'CT', 'SIG0', 'N2', 'PV', 'PTEMP'], inplace: bool = True):
        """ Add TEOS10 variables to the dataset

        By default, adds: 'SA', 'CT', 'SIG0', 'N2', 'PV', 'PTEMP'
        Relies on the gsw library.

        If one exists, the correct CF standard name will be added to the attrs.

        Parameters
        ----------
        vlist: list(str)
            List with the name of variables to add.
            Must be a list containing one or more of the following string values:

            * `"SA"`
                Adds an absolute salinity variable
            * `"CT"`
                Adds a conservative temperature variable
            * `"SIG0"`
                Adds a potential density anomaly variable referenced to 0 dbar
            * `"N2"`
                Adds a buoyancy (Brunt-Vaisala) frequency squared variable.
                This variable has been regridded to the original pressure levels in the Dataset using a linear interpolation.
            * `"PV"`
                Adds a planetary vorticity variable calculated from :math:`\\frac{f N^2}{\\text{gravity}}`.
                This is not a TEOS-10 variable from the gsw toolbox, but is provided for convenience.
                This variable has been regridded to the original pressure levels in the Dataset using a linear interpolation.
            * `"PTEMP"`
                Adds a potential temperature variable
            
        inplace: boolean, True by default
            If True, return the input :class:`xarray.Dataset` with new TEOS10 variables added as a new :class:`xarray.DataArray`
            If False, return a :class:`xarray.Dataset` with new TEOS10 variables

        Returns
        -------
        :class:`xarray.Dataset`
        """
        if not with_gsw:
            raise ModuleNotFoundError(
                "This functionality requires the gsw library")

        allowed = ['SA', 'CT', 'SIG0', 'N2', 'PV', 'PTEMP']
        if any(var not in allowed for var in vlist):
            raise ValueError(f"vlist must be a subset of {allowed}, instead found {vlist}")

        this = self._obj

        to_profile = False
        if self._type == 'profile':
            to_profile = True
            this = this.argo.profile2point()

        # Get base variables as numpy arrays:
        psal = this['PSAL'].values
        temp = this['TEMP'].values
        pres = this['PRES'].values
        lon = this['LONGITUDE'].values
        lat = this['LATITUDE'].values
        f = lat

        # Coriolis
        f = gsw.f(lat)

        # Absolute salinity
        sa = gsw.SA_from_SP(psal, pres, lon, lat)

        # Conservative temperature
        ct = gsw.CT_from_t(sa, temp, pres)

        # Potential Temperature
        if 'PTEMP' in vlist:
            pt = gsw.pt_from_CT(sa, ct)

        # Potential density referenced to surface
        if 'SIG0' in vlist:
            sig0 = gsw.sigma0(sa, ct)

        # N2
        if 'N2' in vlist or 'PV' in vlist:
            n2_mid, p_mid = gsw.Nsquared(sa, ct, pres, lat)
            # N2 on the CT grid:
            ishallow = (slice(0, -1), Ellipsis)
            ideep = (slice(1, None), Ellipsis)

            def mid(x):
                return 0.5 * (x[ideep] + x[ishallow])

            n2 = np.zeros(ct.shape) * np.nan
            n2[1:-1] = mid(n2_mid)

        # PV:
        if 'PV' in vlist:
            pv = f * n2 / gsw.grav(lat, pres)

        # Back to the dataset:
        that = []
        if 'SA' in vlist:
            SA = xr.DataArray(sa, coords=this['PSAL'].coords, name='SA')
            SA.attrs['long_name'] = 'Absolute Salinity'
            SA.attrs['standard_name'] = 'sea_water_absolute_salinity'
            SA.attrs['unit'] = 'g/kg'
            that.append(SA)

        if 'CT' in vlist:
            CT = xr.DataArray(ct, coords=this['TEMP'].coords, name='CT')
            CT.attrs['long_name'] = 'Conservative Temperature'
            CT.attrs['standard_name'] = 'sea_water_conservative_temperature'
            CT.attrs['unit'] = 'degC'
            that.append(CT)

        if 'SIG0' in vlist:
            SIG0 = xr.DataArray(sig0, coords=this['TEMP'].coords, name='SIG0')
            SIG0.attrs['long_name'] = 'Potential density anomaly with reference pressure of 0 dbar'
            SIG0.attrs['standard_name'] = 'sea_water_sigma_theta'
            SIG0.attrs['unit'] = 'kg/m^3'
            that.append(SIG0)

        if 'N2' in vlist:
            N2 = xr.DataArray(n2, coords=this['TEMP'].coords, name='N2')
            N2.attrs['long_name'] = 'Squared buoyancy frequency'
            N2.attrs['unit'] = '1/s^2'
            that.append(N2)

        if 'PV' in vlist:
            PV = xr.DataArray(pv, coords=this['TEMP'].coords, name='PV')
            PV.attrs['long_name'] = 'Planetary Potential Vorticity'
            PV.attrs['unit'] = '1/m/s'
            that.append(PV)

        if 'PTEMP' in vlist:
            PTEMP = xr.DataArray(pt, coords=this['TEMP'].coords, name='PTEMP')
            PTEMP.attrs['long_name'] = 'Potential Temperature'
            PTEMP.attrs['standard_name'] = 'sea_water_potential_temperature'
            PTEMP.attrs['unit'] = 'degC'
            that.append(PTEMP)

        # Create a dataset with all new variables:
        that = xr.merge(that)
        # Add to the dataset essential Argo variables (allows to keep using the argo accessor):
        that = that.assign({k: this[k] for k in ['TIME', ' LATITUDE', 'LONGITUDE', 'PRES', 'PRES_ADJUSTED',
                                                 'PLATFORM_NUMBER', 'CYCLE_NUMBER', 'DIRECTION'] if k in this})
        # Manage output:
        if inplace:
            # Merge previous with new variables
            for v in that.variables:
                this[v] = that[v]
            if to_profile:
                this = this.argo.point2profile()
            for k in this:
                if k not in self._obj:
                    self._obj[k] = this[k]
            return self._obj
        else:
            if to_profile:
                return that.argo.point2profile()
            else:
                return that
Example #10
0
def load_seals(sealdir, bathy_data):
    from datetime import datetime, date, timedelta
    # initialize
    maxNprof = 0
    ll = 0
    ll1 = 0
    l0 = 0
    dateFlNum = []

    XC = bathy_data[0]
    YC = bathy_data[1]
    x1 = bathy_data[3]
    x2 = bathy_data[4]
    y1 = bathy_data[5]
    y2 = bathy_data[6]

    # initialize big arrays
    CT_dataSO = np.nan * np.ones((100 * 310, 1000), '>f4')
    SA_dataSO = np.nan * np.ones((100 * 310, 1000), '>f4')
    SP_dataSO = np.nan * np.ones((100 * 310, 1000), '>f4')
    lon_dataSO = np.nan * np.ones((100 * 310), '>f4')
    lat_dataSO = np.nan * np.ones((100 * 310), '>f4')
    pr_dataSO = np.nan * np.ones((100 * 1000 * 310), '>f4')
    yySO = np.zeros((100 * 310), 'int32')
    mmSO = np.zeros((100 * 310), 'int32')
    IDSO = np.zeros((100 * 310), 'int32')

    data_list = [
        g for g in os.listdir(seadir)
        if g.endswith('nc') and not g.startswith('WOD')
    ]
    for ff_SO in data_list:
        #printff_SO
        tot_fl = 0
        iniDate = datetime(1950, 1, 1)
        minDate = datetime(2019, 1, 1).toordinal()
        #printff_SO
        file = os.path.join(sealdir, '%s' % ff_SO)
        # load data
        data = nc.Dataset(file)
        cruise = data.variables['PLATFORM_NUMBER'][:]
        if "CTD2020" in ff_SO:
            cruise = np.array(['202020%0.3i' % (int(f)) for f in cruise])
        elif "CTD2019" in ff_SO:
            cruise = np.array(['191919%0.3i' % (int(f)) for f in cruise])
        else:
            cruise = np.array(['%0.9i' % f for f in cruise])
        time = data.variables['JULD'][:]
        lon = data.variables['LONGITUDE'][:]
        lat = data.variables['LATITUDE'][:]
        pressPre = data.variables['PRES_ADJUSTED'][:].transpose()
        tempPre = data.variables['TEMP_ADJUSTED'][:].transpose()
        tempQC = data.variables['TEMP_ADJUSTED_QC'][:].transpose()
        psaPre = data.variables['PSAL_ADJUSTED'][:].transpose()
        psaQC = data.variables['PSAL_ADJUSTED_QC'][:].transpose()
        psaPre[np.where(psaQC == '4')] = np.nan
        tempPre[np.where(tempQC == '4')] = np.nan

        # let's get rid of some profiles that are out of the Amudsen Sea
        tot_fl += 1
        msk1 = np.where(np.logical_and(lon >= XC[x1], lon < XC[x2]))[0][:]
        msk2 = np.where(np.logical_and(lat[msk1] > YC[y1],
                                       lat[msk1] < YC[y2]))[0][:]

        lon = lon[msk1][msk2]
        lat = lat[msk1][msk2]
        time = time[msk1][msk2]
        pressPre = pressPre[:, msk1[msk2]]
        tempPre = tempPre[:, msk1[msk2]]
        psaPre = psaPre[:, msk1[msk2]]
        cruise = cruise[msk1][msk2]
        if time[0] + iniDate.toordinal() < minDate:
            minDate = time[0]
        if len(lon) > maxNprof:
            maxNprof = len(lon[~np.isnan(lon)])
            flN = ff_SO
        # remove data below 1000m (there are just so few seal profiles with data below 1000m anyway)
        msk = np.where(pressPre > 1000)
        pressPre[msk] = np.nan
        tempPre[msk] = np.nan
        psaPre[msk] = np.nan

        # interpolate and prepare the data
        lat = np.ma.masked_less(lat, -90)
        lon = np.ma.masked_less(lon, -500)
        lon[lon > 360.] = lon[lon > 360.] - 360.
        Nprof = np.linspace(1, len(lat), len(lat))
        # turn the variables upside down, to have from the surface to depth and not viceversa
        if any(pressPre[:10, 0] > 500.):
            pressPre = pressPre[::-1, :]
            psaPre = psaPre[::-1, :]
            tempPre = tempPre[::-1, :]
        # interpolate data on vertical grid with 1db of resolution (this is fundamental to then create profile means)
        fields = [psaPre, tempPre]
        press = np.nan * np.ones((1000, pressPre.shape[1]))
        for kk in range(press.shape[1]):
            press[:, kk] = np.arange(2, 1002, 1)
        psa = np.nan * np.ones((press.shape), '>f4')
        inst_temp = np.nan * np.ones((press.shape), '>f4')

        for ii, ff in enumerate(fields):
            for nn in range(pressPre.shape[1]):
                # only use non-nan values, otherwise it doesn't interpolate well
                try:
                    f1 = ff[:, nn][ff[:, nn].mask ==
                                   False]  #ff[:,nn][~np.isnan(ff[:,nn])]
                    f2 = pressPre[:, nn][ff[:, nn].mask == False]
                except:
                    f1 = ff[:, nn]
                    f2 = pressPre[:, nn]
                if len(f1) == 0:
                    f1 = ff[:, nn]
                    f2 = pressPre[:, nn]
                try:
                    sp = interpolate.interp1d(f2[~np.isnan(f1)],
                                              f1[~np.isnan(f1)],
                                              kind='linear',
                                              bounds_error=False,
                                              fill_value=np.nan)
                    ff_int = sp(press[:, nn])
                    if ii == 0:
                        psa[:, nn] = ff_int
                    elif ii == 1:
                        inst_temp[:, nn] = ff_int
                except:
                    print(
                        'At profile number %i, the float %s has only 1 record valid: len(f2)=%i'
                        % (nn, ff_SO, len(f2)))

        # To compute theta, I need absolute salinity [g/kg] from practical salinity (PSS-78) [unitless] and conservative temperature.
        sa = np.nan * np.ones((press.shape), '>f4')
        for kk in range(press.shape[1]):
            sa[:, kk] = gsw.SA_from_SP(psa[:, kk], press[:, 0], lon[kk],
                                       lat[kk])
        temp = gsw.CT_from_t(sa, inst_temp, press)
        ptemp = gsw.pt_from_CT(sa, temp)

        # mask out the profiles with :
        msk = np.where(lat < -1000)
        lat[msk] = np.nan
        lon[msk] = np.nan
        cruise[msk] = np.nan
        psa[msk] = np.nan
        sa[msk] = np.nan
        psa[sa == 0.] = np.nan
        sa[sa == 0.] = np.nan
        # use CT instead of PT
        temp[msk] = np.nan
        temp[temp == 0.] = np.nan

        # save the nprofiles
        NN = np.ones((temp.shape), 'int32')
        for ii in range(len(Nprof)):
            NN[:, ii] = Nprof[ii]

        lon_dataSO[ll1:ll1 + len(lon)] = lon
        lat_dataSO[ll1:ll1 + len(lon)] = lat
        CT_dataSO[ll:ll + len(sa[0, :]), :] = temp.T
        SA_dataSO[ll:ll + len(sa[0, :]), :] = sa.T
        SP_dataSO[ll:ll + len(sa[0, :]), :] = psa.T
        IDSO[ll1:ll1 + len(lon)] = [int(f) for f in cruise]

        # separate seasons
        dateFl = []
        for dd in time:
            floatDate = iniDate + timedelta(float(dd))
            dateFl.append(floatDate)
            dateFlNum = np.append(dateFlNum, floatDate.toordinal())

        yearsSO = np.array([int(dd.year) for dd in dateFl])
        monthsSO = np.array([int(dd.month) for dd in dateFl])
        SPR = np.where(np.logical_and(monthsSO >= 9, monthsSO <= 11))
        SUM = np.where(np.logical_or(monthsSO == 12, monthsSO <= 2))
        AUT = np.where(np.logical_and(monthsSO >= 3, monthsSO <= 5))
        WIN = np.where(np.logical_and(monthsSO >= 6, monthsSO <= 8))
        mmFlSO = monthsSO.copy()
        mmFlSO[SPR] = 1
        mmFlSO[SUM] = 2
        mmFlSO[AUT] = 3
        mmFlSO[WIN] = 4

        mmSO[ll:ll + len(sa[0, :])] = mmFlSO
        yySO[ll:ll + len(sa[0, :])] = yearsSO

        ll = ll + len(sa[0, :])
        ll1 = ll1 + len(lon)

        minArgoDate = timedelta(float(minDate)) + iniDate
        minArgoDate.strftime('%Y/%m/%d %H:%M:%S%z')
    # chop away the part of the array with no data
    CT_dataSO = CT_dataSO[:ll, :]
    SA_dataSO = SA_dataSO[:ll, :]
    SP_dataSO = SP_dataSO[:ll, :]
    IDSO = IDSO[:ll]
    lat_dataSO = lat_dataSO[:ll]
    lon_dataSO = lon_dataSO[:ll]
    mmSO = mmSO[:ll]
    yySO = yySO[:ll]

    # remove entire columns of NaNs
    idBad = []
    for ii in range(CT_dataSO.shape[1]):
        f0 = CT_dataSO[:, ii]
        f1 = f0[~np.isnan(f0)]
        if len(f1) == 0:
            idBad.append(ii)

    CT_dataSO = np.delete(CT_dataSO, idBad, 0)
    SA_dataSO = np.delete(SA_dataSO, idBad, 0)
    SP_dataSO = np.delete(SP_dataSO, idBad, 0)
    IDSO = np.delete(IDSO, idBad, 0)
    lon_dataSO = np.delete(lon_dataSO, idBad, 0)
    lat_dataSO = np.delete(lat_dataSO, idBad, 0)
    mmSO = np.delete(mmSO, idBad, 0)
    yySO = np.delete(yySO, idBad, 0)

    load_seals.profSO = [
        CT_dataSO, SA_dataSO, lon_dataSO, lat_dataSO, IDSO, SP_dataSO
    ]
    load_seals.temporalSO = [yySO, mmSO]

    return [load_seals.profSO, load_seals.temporalSO]
Example #11
0
def IO_argo(floatdir):
    from datetime import datetime, date, timedelta
    programs = os.listdir(floatdir)
    programs = programs[1:]
    # initialize
    tot_fl = 0
    maxNprof = 0
    ll = 0
    ll1 = 0
    l0 = 0
    dateFlNum = []
    iniDate = datetime(1950, 1, 1)
    minDate = datetime(2019, 1, 1).toordinal()

    # initialize big arrays
    PT_dataSO = np.nan * np.ones((822 * 310, 2000), '>f4')
    SA_dataSO = np.nan * np.ones((822 * 310, 2000), '>f4')
    lon_dataSO = np.nan * np.ones((822 * 310), '>f4')
    lat_dataSO = np.nan * np.ones((822 * 310), '>f4')
    pr_dataSO = np.nan * np.ones((822 * 2000 * 310), '>f4')
    yySO = np.zeros((822 * 310), 'int32')
    IDSO = np.zeros((822 * 2000 * 310), 'int32')

    for pp in programs:
        SO_floats = os.listdir(os.path.join(floatdir, '%s' % pp))
        for ff_SO in SO_floats:
            print ff_SO
            # @hidden_cell
            SO_prof = [
                f for f in os.listdir(
                    os.path.join(floatdir, '%s/%s' % (pp, ff_SO)))
                if 'prof.nc' in f
            ]

            tot_fl += 1
            file = os.path.join(floatdir, '%s/%s/%s' % (pp, ff_SO, SO_prof[0]))
            data = nc.Dataset(file)
            time = data.variables['JULD'][:]
            lon = data.variables['LONGITUDE'][:]
            lat = data.variables['LATITUDE'][:]
            pressPre = data.variables['PRES_ADJUSTED'][:].transpose()
            tempPre = data.variables['TEMP_ADJUSTED'][:].transpose()
            tempQC = data.variables['TEMP_ADJUSTED_QC'][:].transpose()
            psaPre = data.variables['PSAL_ADJUSTED'][:].transpose()
            psaQC = data.variables['PSAL_ADJUSTED_QC'][:].transpose()
            # have to add the good QC: [1,2,5,8]  (not in ['1','2','5','8'])
            psaPre[np.where(psaQC != '1')] = np.nan
            tempPre[np.where(tempQC != '1')] = np.nan
            # let's get rid of some profiles that are out of the indian sector
            msk1 = np.where(np.logical_and(lon >= 0, lon < 180))[0][:]
            msk2 = np.where(np.logical_and(lat[msk1] > -70,
                                           lat[msk1] < -30))[0][:]

            lon = lon[msk1][msk2]
            lat = lat[msk1][msk2]
            time = time[msk1][msk2]
            pressPre = pressPre[:, msk1[msk2]]
            tempPre = tempPre[:, msk1[msk2]]
            psaPre = psaPre[:, msk1[msk2]]
            if time[0] + iniDate.toordinal() < minDate:
                minDate = time[0]
            if len(lon) > maxNprof:
                maxNprof = len(lon[~np.isnan(lon)])
                flN = ff_SO

            # interpolate
            lat = np.ma.masked_less(lat, -90)
            lon = np.ma.masked_less(lon, -500)
            lon[lon > 360.] = lon[lon > 360.] - 360.
            Nprof = np.linspace(1, len(lat), len(lat))
            # turn the variables upside down, to have from the surface to depth and not viceversa
            if any(pressPre[:10, 0] > 500.):
                pressPre = pressPre[::-1, :]
                psaPre = psaPre[::-1, :]
                tempPre = tempPre[::-1, :]
            # interpolate data on vertical grid with 1db of resolution (this is fundamental to then create profile means)
            fields = [psaPre, tempPre]
            press = np.nan * np.ones((2000, pressPre.shape[1]))
            for kk in range(press.shape[1]):
                press[:, kk] = np.arange(2, 2002, 1)
            psa = np.nan * np.ones((press.shape), '>f4')
            temp = np.nan * np.ones((press.shape), '>f4')

            for ii, ff in enumerate(fields):
                for nn in range(pressPre.shape[1]):
                    # only use non-nan values, otherwise it doesn't interpolate well
                    try:
                        f1 = ff[:, nn][ff[:, nn].mask ==
                                       False]  #ff[:,nn][~np.isnan(ff[:,nn])]
                        f2 = pressPre[:, nn][ff[:, nn].mask == False]
                    except:
                        f1 = ff[:, nn]
                        f2 = pressPre[:, nn]
                    if len(f1) == 0:
                        f1 = ff[:, nn]
                        f2 = pressPre[:, nn]
                    try:
                        sp = interpolate.interp1d(f2[~np.isnan(f1)],
                                                  f1[~np.isnan(f1)],
                                                  kind='linear',
                                                  bounds_error=False,
                                                  fill_value=np.nan)
                        ff_int = sp(press[:, nn])
                        if ii == 0:
                            psa[:, nn] = ff_int
                        elif ii == 1:
                            temp[:, nn] = ff_int
                    except:
                        continue
                    #print 'At profile number %i, the float %s has only 1 record valid'

            # To compute theta, I need absolute salinity [g/kg] from practical salinity (PSS-78) [unitless] and conservative temperature.
            sa = np.nan * np.ones((press.shape), '>f4')
            for kk in range(press.shape[1]):
                sa[:, kk] = gsw.SA_from_SP(psa[:, kk], press[:, 0], lon[kk],
                                           lat[kk])
            ptemp = gsw.pt_from_CT(sa, temp)

            # mask out the profiles with :
            msk = np.where(lat < -1000)
            lat[msk] = np.nan
            lon[msk] = np.nan
            sa[msk] = np.nan
            sa[sa == 0.] = np.nan
            ptemp[msk] = np.nan
            ptemp[temp == 0.] = np.nan

            # save the nprofiles
            NN = np.ones((temp.shape), 'int32')
            for ii in range(len(Nprof)):
                NN[:, ii] = Nprof[ii]

            lon_dataSO[ll1:ll1 + len(lon)] = lon
            lat_dataSO[ll1:ll1 + len(lon)] = lat
            PT_dataSO[ll:ll + len(sa[0, :]), :] = ptemp.T
            SA_dataSO[ll:ll + len(sa[0, :]), :] = sa.T
            floatID = int(ff_SO) * np.ones((sa.shape[1]), 'int32')
            IDSO[ll:ll + len(sa[0, :])] = floatID

            # separate seasons
            dateFl = []
            for dd in time:
                floatDate = iniDate + timedelta(float(dd))
                dateFl.append(floatDate)
                dateFlNum = np.append(dateFlNum, floatDate.toordinal())

            yearsSO = np.array([int(dd.year) for dd in dateFl])
            yySO[ll:ll + len(sa[0, :])] = yearsSO

            ll = ll + len(sa[0, :])
            ll1 = ll1 + len(lon)

    # chop away the part of the array with no data
    PT_dataSO = PT_dataSO[:ll, :]
    SA_dataSO = SA_dataSO[:ll, :]
    IDSO = IDSO[:ll]
    lat_dataSO = lat_dataSO[:ll]
    lon_dataSO = lon_dataSO[:ll]
    mmSO = mmSO[:ll]
    yySO = yySO[:ll]

    # remove entire columns of NaNs
    idBad = []
    for ii in range(PT_dataSO.shape[0]):
        f0 = PT_dataSO[ii, :]
        f1 = f0[~np.isnan(f0)]
        if len(f1) == 0:
            idBad.append(ii)
    PT_dataSO = np.delete(PT_dataSO, idBad, 0)
    SA_dataSO = np.delete(SA_dataSO, idBad, 0)
    IDSO = np.delete(IDSO, idBad, 0)
    lon_dataSO = np.delete(lon_dataSO, idBad, 0)
    lat_dataSO = np.delete(lat_dataSO, idBad, 0)
    mmSO = np.delete(mmSO, idBad, 0)
    yySO = np.delete(yySO, idBad, 0)

    idBad = []
    # Interpolate again in depth.. for some reason, some profiles have still wholes in the middle:
    for ii in range(PT_dataSO.shape[0]):
        f0 = PT_dataSO[ii, :]
        try:
            sp = interpolate.interp1d(press[~np.isnan(f0), 0],
                                      f0[~np.isnan(f0)],
                                      kind='linear',
                                      bounds_error=False,
                                      fill_value=np.nan)
            PT_dataSO[ii, :] = sp(press[:, 0])
            f0 = SA_dataSO[ii, :]
            sp = interpolate.interp1d(press[~np.isnan(f0), 0],
                                      f0[~np.isnan(f0)],
                                      kind='linear',
                                      bounds_error=False,
                                      fill_value=np.nan)
            SA_dataSO[ii, :] = sp(press[:, 0])
        except:
            idBad.append(ii)
            #print ii, ' has only 1 number'
    PT_dataSO = np.delete(PT_dataSO, idBad, 0)
    SA_dataSO = np.delete(SA_dataSO, idBad, 0)
    IDSO = np.delete(IDSO, idBad, 0)
    lon_dataSO = np.delete(lon_dataSO, idBad, 0)
    lat_dataSO = np.delete(lat_dataSO, idBad, 0)
    mmSO = np.delete(mmSO, idBad, 0)
    yySO = np.delete(yySO, idBad, 0)

    IO_argo.profSO = [PT_dataSO, SA_dataSO, lon_dataSO, lat_dataSO, IDSO]
    IO_argo.temporalSO = [yySO, mmSO]

    return [IO_argo.profSO, IO_argo.temporalSO]
Example #12
0
    def teos10(self,
               vlist: list = ['SA', 'CT', 'SIG0', 'N2', 'PV', 'PTEMP'],
               inplace: bool = True):
        """ Add TEOS10 variables to the dataset

        By default, add: 'SA', 'CT', 'SIG0', 'N2', 'PV', 'PTEMP'
        Rely on the gsw library.

        Parameters
        ----------
        vlist: list(str)
            List with the name of variables to add.
        inplace: boolean, True by default
            If True, return the input :class:`xarray.Dataset` with new TEOS10 variables added as a new :class:`xarray.DataArray`
            If False, return a :class:`xarray.Dataset` with new TEOS10 variables

        Returns
        -------
        :class:`xarray.Dataset`
        """
        if not with_gsw:
            raise ModuleNotFoundError(
                "This functionality requires the gsw library")

        this = self._obj

        to_profile = False
        if self._type == 'profile':
            to_profile = True
            this = this.argo.profile2point()

        # Get base variables as numpy arrays:
        psal = this['PSAL'].values
        temp = this['TEMP'].values
        pres = this['PRES'].values
        lon = this['LONGITUDE'].values
        lat = this['LATITUDE'].values
        f = lat

        # Coriolis
        f = gsw.f(lat)

        # Depth:
        depth = gsw.z_from_p(pres, lat)

        # Absolute salinity
        sa = gsw.SA_from_SP(psal, pres, lon, lat)

        # Conservative temperature
        ct = gsw.CT_from_t(sa, temp, depth)

        # Potential Temperature
        if 'PTEMP' in vlist:
            pt = gsw.pt_from_CT(sa, ct)

        # Potential density referenced to surface
        if 'SIG0' in vlist:
            sig0 = gsw.sigma0(sa, ct)

        # N2
        if 'N2' in vlist or 'PV' in vlist:
            n2_mid, p_mid = gsw.Nsquared(sa, ct, pres, lat)
            # N2 on the CT grid:
            ishallow = (slice(0, -1), Ellipsis)
            ideep = (slice(1, None), Ellipsis)

            def mid(x):
                return 0.5 * (x[ideep] + x[ishallow])

            n2 = np.zeros(ct.shape) * np.nan
            n2[1:-1] = mid(n2_mid)

        # PV:
        if 'PV' in vlist:
            pv = f * n2 / gsw.grav(lat, pres)

        # Back to the dataset:
        that = []
        if 'SA' in vlist:
            SA = xr.DataArray(sa, coords=this['PSAL'].coords, name='SA')
            SA.attrs['standard_name'] = 'Absolute Salinity'
            SA.attrs['unit'] = 'g/kg'
            that.append(SA)

        if 'CT' in vlist:
            CT = xr.DataArray(ct, coords=this['TEMP'].coords, name='CT')
            CT.attrs['standard_name'] = 'Conservative Temperature'
            CT.attrs['unit'] = 'degC'
            that.append(CT)

        if 'SIG0' in vlist:
            SIG0 = xr.DataArray(sig0, coords=this['TEMP'].coords, name='SIG0')
            SIG0.attrs[
                'long_name'] = 'Potential density anomaly with reference pressure of 0 dbar'
            SIG0.attrs['standard_name'] = 'Potential Density'
            SIG0.attrs['unit'] = 'kg/m^3'
            that.append(SIG0)

        if 'N2' in vlist:
            N2 = xr.DataArray(n2, coords=this['TEMP'].coords, name='N2')
            N2.attrs['standard_name'] = 'Squared buoyancy frequency'
            N2.attrs['unit'] = '1/s^2'
            that.append(N2)

        if 'PV' in vlist:
            PV = xr.DataArray(pv, coords=this['TEMP'].coords, name='PV')
            PV.attrs['standard_name'] = 'Planetary Potential Vorticity'
            PV.attrs['unit'] = '1/m/s'
            that.append(PV)

        if 'PTEMP' in vlist:
            PTEMP = xr.DataArray(pt, coords=this['TEMP'].coords, name='PTEMP')
            PTEMP.attrs['standard_name'] = 'Potential Temperature'
            PTEMP.attrs['unit'] = 'degC'
            that.append(PTEMP)

        # Create a dataset with all new variables:
        that = xr.merge(that)
        # Add to the dataset essential Argo variables (allows to keep using the argo accessor):
        that = that.assign({
            k: this[k]
            for k in [
                'TIME', ' LATITUDE', 'LONGITUDE', 'PRES', 'PRES_ADJUSTED',
                'PLATFORM_NUMBER', 'CYCLE_NUMBER', 'DIRECTION'
            ] if k in this
        })
        # Manage output:
        if inplace:
            # Merge previous with new variables
            for v in that.variables:
                this[v] = that[v]
            if to_profile:
                this = this.argo.point2profile()
            for k in this:
                if k not in self._obj:
                    self._obj[k] = this[k]
            return self._obj
        else:
            if to_profile:
                return that.argo.point2profile()
            else:
                return that
Example #13
0
def compute_transport(historyFileList, casename, meshfile, maskfile, figdir,\
                      transectName='Drake Passage', outfile='transport.nc'):
    mesh = xr.open_dataset(meshfile)
    mask = get_mask_short_names(xr.open_dataset(maskfile))

    if transectName == 'all' or transectName == 'StandardTransportSectionsRegionsGroup':
        transectList = mask.shortNames[:].values
        condition = transectList != "Atlantic Transec"
        transectList = np.extract(condition, transectList)
    else:
        transectList = transectName.split(',')
        if platform.python_version()[0] == '3':
            for i in range(len(transectList)):
                transectList[i] = "b'" + transectList[i]

    print('Computing Transport for the following transects ', transectList)
    nTransects = len(transectList)
    maxEdges = mask.dims['maxEdgesInTransect']
    # Compute refLayerThickness to avoid need for hist file
    refBottom = mesh.refBottomDepth.values
    nz = mesh.dims['nVertLevels']
    h = np.zeros(nz)
    h[0] = refBottom[0]
    for i in range(1, nz):
        h[i] = refBottom[i] - refBottom[i - 1]

    # Get a list of edges and total edges in each transect
    nEdgesInTransect = np.zeros(nTransects)
    edgeVals = np.zeros((nTransects, maxEdges))
    for i in range(nTransects):
        amask = mask.sel(shortNames=transectList[i]).squeeze()
        transectEdges = amask.transectEdgeGlobalIDs.values
        inds = np.where(transectEdges > 0)[0]
        nEdgesInTransect[i] = len(inds)
        transectEdges = transectEdges[inds]
        edgeVals[i, :len(inds)] = np.asarray(transectEdges - 1, dtype='i')

    nEdgesInTransect = np.asarray(nEdgesInTransect, dtype='i')

    # Create a list with the start and stop for transect bounds
    nTransectStartStop = np.zeros(nTransects + 1)
    for j in range(1, nTransects + 1):
        nTransectStartStop[j] = nTransectStartStop[j -
                                                   1] + nEdgesInTransect[j - 1]

    edgesToRead = edgeVals[0, :nEdgesInTransect[0]]
    for i in range(1, nTransects):
        edgesToRead = np.hstack(
            [edgesToRead, edgeVals[i, :nEdgesInTransect[i]]])

    edgesToRead = np.asarray(edgesToRead, dtype='i')
    dvEdge = mesh.dvEdge.sel(nEdges=edgesToRead).values
    cellsOnEdge = mesh.cellsOnEdge.sel(nEdges=edgesToRead).values
    edgeSigns = np.zeros((nTransects, len(edgesToRead)))
    for i in range(nTransects):
        edgeSigns[i, :] = mask.sel(
            nEdges=edgesToRead,
            shortNames=transectList[i]).squeeze().transectEdgeMaskSigns.values

    latmean = 180.0 / np.pi * np.nanmean(
        mesh.latEdge.sel(nEdges=edgesToRead).values)
    lonmean = 180.0 / np.pi * np.nanmean(
        mesh.lonEdge.sel(nEdges=edgesToRead).values)
    pressure = gsw.p_from_z(-refBottom, latmean)

    # Read history files one at a time and slice
    fileList = sorted(glob.glob(historyFileList))
    vol_transport = np.zeros((len(fileList), nTransects))
    vol_transportIn = np.zeros((len(fileList), nTransects))
    vol_transportOut = np.zeros((len(fileList), nTransects))
    heat_transport = np.zeros((len(fileList), nTransects))  # Tref = 0degC
    heat_transportIn = np.zeros((len(fileList), nTransects))
    heat_transportOut = np.zeros((len(fileList), nTransects))
    heat_transportTfp = np.zeros(
        (len(fileList),
         nTransects))  # Tref = T freezing point computed below (Tfp)
    heat_transportTfpIn = np.zeros((len(fileList), nTransects))
    heat_transportTfpOut = np.zeros((len(fileList), nTransects))
    salt_transport = np.zeros((len(fileList), nTransects))  # Sref = saltRef
    salt_transportIn = np.zeros((len(fileList), nTransects))
    salt_transportOut = np.zeros((len(fileList), nTransects))
    t = np.zeros(len(fileList))
    for i, fname in enumerate(fileList):
        ncid = Dataset(fname, 'r')
        if 'timeMonthly_avg_normalTransportVelocity' in ncid.variables.keys():
            vel = ncid.variables['timeMonthly_avg_normalTransportVelocity'][
                0, edgesToRead, :]
        elif 'timeMonthly_avg_normalVelocity' in ncid.variables.keys():
            vel = ncid.variables['timeMonthly_avg_normalVelocity'][
                0, edgesToRead, :]
            if 'timeMonthly_avg_normalGMBolusVelocity' in ncid.variables.keys(
            ):
                vel += ncid.variables['timeMonthly_avg_normalGMBolusVelocity'][
                    0, edgesToRead, :]
        else:
            raise KeyError('no appropriate normalVelocity variable found')
        tempOnCell1 = ncid.variables[
            'timeMonthly_avg_activeTracers_temperature'][0, cellsOnEdge[:, 0] -
                                                         1, :]
        tempOnCell2 = ncid.variables[
            'timeMonthly_avg_activeTracers_temperature'][0, cellsOnEdge[:, 1] -
                                                         1, :]
        saltOnCell1 = ncid.variables['timeMonthly_avg_activeTracers_salinity'][
            0, cellsOnEdge[:, 0] - 1, :]
        saltOnCell2 = ncid.variables['timeMonthly_avg_activeTracers_salinity'][
            0, cellsOnEdge[:, 1] - 1, :]
        t[i] = ncid.variables['timeMonthly_avg_daysSinceStartOfSim'][:] / 365.
        ncid.close()
        # Mask T,S values that fall on land
        tempOnCell1[cellsOnEdge[:, 0] == 0, :] = np.nan
        tempOnCell2[cellsOnEdge[:, 1] == 0, :] = np.nan
        saltOnCell1[cellsOnEdge[:, 0] == 0, :] = np.nan
        saltOnCell2[cellsOnEdge[:, 1] == 0, :] = np.nan
        # Interpolate T,S values onto edges
        tempOnEdge = np.nanmean(np.array([tempOnCell1, tempOnCell2]), axis=0)
        saltOnEdge = np.nanmean(np.array([saltOnCell1, saltOnCell2]), axis=0)
        # Mask values that fall onto topography
        tempOnEdge[np.logical_or(tempOnEdge > 1e15,
                                 tempOnEdge < -1e15)] = np.nan
        saltOnEdge[np.logical_or(saltOnEdge > 1e15,
                                 saltOnEdge < -1e15)] = np.nan

        # Compute freezing temperature
        SA = gsw.SA_from_SP(saltOnEdge, pressure, lonmean, latmean)
        CTfp = gsw.CT_freezing(SA, pressure, 0.)
        Tfp = gsw.pt_from_CT(SA, CTfp)

        # Compute transports for each transect
        for j in range(nTransects):
            start = int(nTransectStartStop[j])
            stop = int(nTransectStartStop[j + 1])
            dArea = dvEdge[start:stop, np.newaxis] * h[np.newaxis, :]
            normalVel = vel[start:stop, :] * edgeSigns[j, start:stop,
                                                       np.newaxis]
            temp = tempOnEdge[start:stop, :]
            salt = saltOnEdge[start:stop, :]
            tfreezing = Tfp[start:stop, :]
            indVelP = np.where(normalVel > 0)
            indVelM = np.where(normalVel < 0)
            vol_transport[i, j] = np.nansum(np.nansum(normalVel * dArea))
            vol_transportIn[i, j] = np.nansum(
                np.nansum(normalVel[indVelP] * dArea[indVelP]))
            vol_transportOut[i, j] = np.nansum(
                np.nansum(normalVel[indVelM] * dArea[indVelM]))
            heat_transport[i,
                           j] = np.nansum(np.nansum(temp * normalVel * dArea))
            heat_transportIn[i, j] = np.nansum(
                np.nansum(temp[indVelP] * normalVel[indVelP] * dArea[indVelP]))
            heat_transportOut[i, j] = np.nansum(
                np.nansum(temp[indVelM] * normalVel[indVelM] * dArea[indVelM]))
            heat_transportTfp[i, j] = np.nansum(
                np.nansum((temp - tfreezing) * normalVel * dArea))
            heat_transportTfpIn[i, j] = np.nansum(
                np.nansum((temp[indVelP] - tfreezing[indVelP]) *
                          normalVel[indVelP] * dArea[indVelP]))
            heat_transportTfpOut[i, j] = np.nansum(
                np.nansum((temp[indVelM] - tfreezing[indVelM]) *
                          normalVel[indVelM] * dArea[indVelM]))
            salt_transport[i,
                           j] = np.nansum(np.nansum(salt * normalVel * dArea))
            salt_transport[
                i, j] = vol_transport[i, j] - salt_transport[i, j] / saltRef
            salt_transportIn[i, j] = np.nansum(
                np.nansum(salt[indVelP] * normalVel[indVelP] * dArea[indVelP]))
            salt_transportIn[
                i,
                j] = vol_transportIn[i, j] - salt_transportIn[i, j] / saltRef
            salt_transportOut[i, j] = np.nansum(
                np.nansum(salt[indVelM] * normalVel[indVelM] * dArea[indVelM]))
            salt_transportOut[
                i,
                j] = vol_transportOut[i, j] - salt_transportOut[i, j] / saltRef
    vol_transport = m3ps_to_Sv * vol_transport
    vol_transportIn = m3ps_to_Sv * vol_transportIn
    vol_transportOut = m3ps_to_Sv * vol_transportOut
    heat_transport = W_to_TW * rhoRef * cp * heat_transport
    heat_transportIn = W_to_TW * rhoRef * cp * heat_transportIn
    heat_transportOut = W_to_TW * rhoRef * cp * heat_transportOut
    heat_transportTfp = W_to_TW * rhoRef * cp * heat_transportTfp
    heat_transportTfpIn = W_to_TW * rhoRef * cp * heat_transportTfpIn
    heat_transportTfpOut = W_to_TW * rhoRef * cp * heat_transportTfpOut
    salt_transport = m3ps_to_km3py * salt_transport
    salt_transportIn = m3ps_to_km3py * salt_transportIn
    salt_transportOut = m3ps_to_km3py * salt_transportOut

    # Define some dictionaries for transect plotting
    obsDict = {'Drake Passage':[120, 175], 'Tasmania-Ant':[147, 167], 'Africa-Ant':None, 'Antilles Inflow':[-23.1, -13.7], \
               'Mona Passage':[-3.8, -1.4],'Windward Passage':[-7.2, -6.8], 'Florida-Cuba':[30, 33], 'Florida-Bahamas':[30, 33], \
               'Indonesian Throughflow':[-21, -11], 'Agulhas':[-90, -50], 'Mozambique Channel':[-20, -8], \
               'Bering Strait':[0.6, 1.0], 'Lancaster Sound':[-1.0, -0.5], 'Fram Strait':[-4.7, 0.7], \
               'Robeson Channel':None, 'Davis Strait':[-1.6, -3.6], 'Barents Sea Opening':[1.4, 2.6], \
               'Nares Strait':[-1.8, 0.2], 'Denmark Strait':None, 'Iceland-Faroe-Scotland':None}
    labelDict = {'Drake Passage':'drake', 'Tasmania-Ant':'tasmania', 'Africa-Ant':'africaAnt', 'Antilles Inflow':'antilles', \
                 'Mona Passage':'monaPassage', 'Windward Passage':'windwardPassage', 'Florida-Cuba':'floridaCuba', \
                 'Florida-Bahamas':'floridaBahamas', 'Indonesian Throughflow':'indonesia', 'Agulhas':'agulhas', \
                 'Mozambique Channel':'mozambique', 'Bering Strait':'beringStrait', 'Lancaster Sound':'lancasterSound', \
                 'Fram Strait':'framStrait', 'Robeson Channel':'robeson', 'Davis Strait':'davisStrait', 'Barents Sea Opening':'barentsSea', \
                 'Nares Strait':'naresStrait', 'Denmark Strait':'denmarkStrait', 'Iceland-Faroe-Scotland':'icelandFaroeScotland'}
    figsize = (20, 10)
    figdpi = 80

    for i in range(nTransects):
        if platform.python_version()[0] == '3':
            searchString = transectList[i][2:]
        else:
            searchString = transectList[i]

        # Plot Volume Transport
        figfile = '{}/volTransport_{}_{}.png'.format(figdir,
                                                     labelDict[searchString],
                                                     casename)
        plt.figure(figsize=figsize, dpi=figdpi)
        bounds = obsDict[searchString]
        plt.plot(t, vol_transport[:, i], 'k', linewidth=2, label='model (net)')
        plt.plot(t,
                 vol_transportIn[:, i],
                 'r',
                 linewidth=2,
                 label='model (inflow)')
        plt.plot(t,
                 vol_transportOut[:, i],
                 'b',
                 linewidth=2,
                 label='model (outflow)')
        if bounds is not None:
            plt.gca().fill_between(t,
                                   bounds[0] * np.ones_like(t),
                                   bounds[1] * np.ones_like(t),
                                   alpha=0.3,
                                   label='observations (net)')
        plt.autoscale(enable=True, axis='x', tight=True)
        plt.ylabel('Volume transport (Sv)', fontsize=12, fontweight='bold')
        plt.xlabel('Time (Years)', fontsize=12, fontweight='bold')
        plt.title('Volume transport for {} ({}, mean (net)={:5.2f} $\pm$ {:5.2f})'.format(searchString, casename, \
                  np.nanmean(vol_transport[:,i]), np.nanstd(vol_transport[:,i]), fontsize=16, fontweight='bold'))
        plt.legend()
        plt.savefig(figfile, bbox_inches='tight')

        # Plot Heat Transport wrt Tref=0
        figfile = '{}/heatTransport_{}_{}.png'.format(figdir,
                                                      labelDict[searchString],
                                                      casename)
        plt.figure(figsize=figsize, dpi=figdpi)
        plt.plot(t,
                 heat_transport[:, i],
                 'k',
                 linewidth=2,
                 label='model (net)')
        plt.plot(t,
                 heat_transportIn[:, i],
                 'r',
                 linewidth=2,
                 label='model (inflow)')
        plt.plot(t,
                 heat_transportOut[:, i],
                 'b',
                 linewidth=2,
                 label='model (outflow)')
        plt.autoscale(enable=True, axis='x', tight=True)
        plt.ylabel('Heat transport wrt 0$^\circ$C (TW)',
                   fontsize=12,
                   fontweight='bold')
        plt.xlabel('Time (Years)', fontsize=12, fontweight='bold')
        plt.title('Heat transport for {} ({}, mean (net)={:5.2f} $\pm$ {:5.2f})'.format(searchString, casename, \
                  np.nanmean(heat_transport[:,i]), np.nanstd(heat_transport[:,i]), fontsize=16, fontweight='bold'))
        plt.legend()
        plt.savefig(figfile, bbox_inches='tight')

        # Plot Heat Transport wrt Tref=tempRef
        figfile = '{}/heatTransportTfp_{}_{}.png'.format(
            figdir, labelDict[searchString], casename)
        plt.figure(figsize=figsize, dpi=figdpi)
        plt.plot(t,
                 heat_transportTfp[:, i],
                 'k',
                 linewidth=2,
                 label='model (net)')
        plt.plot(t,
                 heat_transportTfpIn[:, i],
                 'r',
                 linewidth=2,
                 label='model (inflow)')
        plt.plot(t,
                 heat_transportTfpOut[:, i],
                 'b',
                 linewidth=2,
                 label='model (outflow)')
        plt.autoscale(enable=True, axis='x', tight=True)
        plt.ylabel('Heat transport wrt freezing point (TW)',
                   fontsize=12,
                   fontweight='bold')
        plt.xlabel('Time (Years)', fontsize=12, fontweight='bold')
        plt.title('Heat transport for {} ({}, mean (net)={:5.2f} $\pm$ {:5.2f})'.format(searchString, casename, \
                  np.nanmean(heat_transportTfp[:,i]), np.nanstd(heat_transportTfp[:,i]), fontsize=16, fontweight='bold'))
        plt.legend()
        plt.savefig(figfile, bbox_inches='tight')

        # Plot FW Transport
        figfile = '{}/fwTransport_{}_{}.png'.format(figdir,
                                                    labelDict[searchString],
                                                    casename)
        plt.figure(figsize=figsize, dpi=figdpi)
        plt.plot(t,
                 salt_transport[:, i],
                 'k',
                 linewidth=2,
                 label='model (net)')
        plt.plot(t,
                 salt_transportIn[:, i],
                 'r',
                 linewidth=2,
                 label='model (inflow)')
        plt.plot(t,
                 salt_transportOut[:, i],
                 'b',
                 linewidth=2,
                 label='model (outflow)')
        plt.autoscale(enable=True, axis='x', tight=True)
        plt.ylabel('FW transport (km$^3$/year)',
                   fontsize=12,
                   fontweight='bold')
        plt.xlabel('Time (Years)', fontsize=12, fontweight='bold')
        plt.title('FW transport for {} ({}, mean (net)={:5.2f} $\pm$ {:5.2f})'.format(searchString, casename, \
                  np.nanmean(salt_transport[:,i]), np.nanstd(salt_transport[:,i]), fontsize=16, fontweight='bold'))
        plt.legend()
        plt.savefig(figfile, bbox_inches='tight')

    # Add calls to save transport and then can build up
    ncid = Dataset(outfile, mode='w', clobber=True, format='NETCDF3_CLASSIC')

    # Add calls to save transport and then can build up
    ncid = Dataset(outfile, mode='w', clobber=True, format='NETCDF3_CLASSIC')
    ncid.createDimension('Time', None)
    ncid.createDimension('nTransects', nTransects)
    ncid.createDimension('StrLen', 64)
    transectNames = ncid.createVariable('TransectNames', 'c',
                                        ('nTransects', 'StrLen'))
    times = ncid.createVariable('Time', 'f8', 'Time')
    vol_transportVar = ncid.createVariable('volTransport', 'f8',
                                           ('Time', 'nTransects'))
    vol_transportInVar = ncid.createVariable('volTransportIn', 'f8',
                                             ('Time', 'nTransects'))
    vol_transportOutVar = ncid.createVariable('volTransportOut', 'f8',
                                              ('Time', 'nTransects'))
    heat_transportVar = ncid.createVariable('heatTransport', 'f8',
                                            ('Time', 'nTransects'))
    heat_transportInVar = ncid.createVariable('heatTransportIn', 'f8',
                                              ('Time', 'nTransects'))
    heat_transportOutVar = ncid.createVariable('heatTransportOut', 'f8',
                                               ('Time', 'nTransects'))
    heat_transportTfpVar = ncid.createVariable('heatTransportTfp', 'f8',
                                               ('Time', 'nTransects'))
    heat_transportTfpInVar = ncid.createVariable('heatTransportTfpIn', 'f8',
                                                 ('Time', 'nTransects'))
    heat_transportTfpOutVar = ncid.createVariable('heatTransportTfpOut', 'f8',
                                                  ('Time', 'nTransects'))
    salt_transportVar = ncid.createVariable('FWTransport', 'f8',
                                            ('Time', 'nTransects'))
    salt_transportInVar = ncid.createVariable('FWTransportIn', 'f8',
                                              ('Time', 'nTransects'))
    salt_transportOutVar = ncid.createVariable('FWTransportOut', 'f8',
                                               ('Time', 'nTransects'))

    vol_transportVar.units = 'Sv'
    vol_transportInVar.units = 'Sv'
    vol_transportOutVar.units = 'Sv'
    heat_transportVar.units = 'TW'
    heat_transportInVar.units = 'TW'
    heat_transportOutVar.units = 'TW'
    heat_transportTfpVar.units = 'TW'
    heat_transportTfpInVar.units = 'TW'
    heat_transportTfpOutVar.units = 'TW'
    salt_transportVar.units = 'km^3/year'
    salt_transportInVar.units = 'km^3/year'
    salt_transportOutVar.units = 'km^3/year'

    vol_transportVar.description = 'Net volume transport across transect'
    vol_transportInVar.description = 'Inflow volume transport across transect (in/out determined by edgeSign)'
    vol_transportOutVar.description = 'Outflow volume transport across transect (in/out determined by edgeSign)'
    heat_transportVar.description = 'Net heat transport (wrt 0degC) across transect'
    heat_transportInVar.description = 'Inflow heat transport (wrt 0degC) across transect (in/out determined by edgeSign)'
    heat_transportOutVar.description = 'Outflow heat transport (wrt 0degC) across transect (in/out determined by edgeSign)'
    heat_transportTfpVar.description = 'Net heat transport (wrt freezing point) across transect'
    heat_transportTfpInVar.description = 'Inflow heat transport (wrt freezing point) across transect (in/out determined by edgeSign)'
    heat_transportTfpOutVar.description = 'Outflow heat transport (wrt freezing point) across transect (in/out determined by edgeSign)'
    salt_transportVar.description = 'Net FW transport (wrt {:4.1f} psu) across transect'.format(
        saltRef)
    salt_transportInVar.description = 'Inflow FW transport (wrt {:4.1f} psu) across transect (in/out determined by edgeSign)'.format(
        saltRef)
    salt_transportOutVar.description = 'Outflow FW transport (wrt {:4.1f} psu) across transect (in/out determined by edgeSign)'.format(
        saltRef)

    times[:] = t
    vol_transportVar[:, :] = vol_transport
    vol_transportInVar[:, :] = vol_transportIn
    vol_transportOutVar[:, :] = vol_transportOut
    heat_transportVar[:, :] = heat_transport
    heat_transportInVar[:, :] = heat_transportIn
    heat_transportOutVar[:, :] = heat_transportOut
    heat_transportTfpVar[:, :] = heat_transportTfp
    heat_transportTfpInVar[:, :] = heat_transportTfpIn
    heat_transportTfpOutVar[:, :] = heat_transportTfpOut
    salt_transportVar[:, :] = salt_transport
    salt_transportInVar[:, :] = salt_transportIn
    salt_transportOutVar[:, :] = salt_transportOut

    for i in range(nTransects):
        nLetters = len(transectList[i])
        transectNames[i, :nLetters] = transectList[i]

    ncid.close()