示例#1
0
def read_plot_ts_slice_diff (file_path_1, file_path_2, grid=None, lon0=None, lat0=None, point0=None, point1=None, time_index=None, t_start=None, t_end=None, time_average=False, time_index_2=None, t_start_2=None, t_end_2=None, hmin=None, hmax=None, zmin=None, zmax=None, tmin=None, tmax=None, smin=None, smax=None, tcontours=None, scontours=None, date_string=None, fig_name=None, second_file_path_1=None, second_file_path_2=None):

    diff_time = (time_index_2 is not None) or (time_average and (t_start_2 is not None or t_end_2 is not None))

    grid = choose_grid(grid, file_path_1)
    check_single_time(time_index, time_average)
    date_string = check_date_string(date_string, file_path_1, time_index)

    # Inner function to read a variable from the correct NetCDF file and mask appropriately
    def read_and_mask (var_name, file_path, second_file_path=None, check_diff_time=False):
        # Do we need to choose the right file?
        if second_file_path is not None:
            file_path_use = find_variable(file_path, second_file_path, var_name)
        else:
            file_path_use = file_path
        # Read and mask the data
        if check_diff_time and diff_time:
            return mask_3d(read_netcdf(file_path_use, var_name, time_index=time_index_2, t_start=t_start_2, t_end=t_end_2, time_average=time_average), grid)
        else:
            return mask_3d(read_netcdf(file_path_use, var_name, time_index=time_index, t_start=t_start, t_end=t_end, time_average=time_average), grid)


    # Interface to call read_and_mask for each variable
    def read_and_mask_both (var_name):
        data1 = read_and_mask(var_name, file_path_1, second_file_path=second_file_path_1)
        data2 = read_and_mask(var_name, file_path_2, second_file_path=second_file_path_2, check_diff_time=True)
        return data1, data2

    # Read temperature and salinity for each simulation
    temp_1, temp_2 = read_and_mask_both('THETA')
    salt_1, salt_2 = read_and_mask_both('SALT')

    # Plot
    ts_slice_plot_diff(temp_1, temp_2, salt_1, salt_2, grid, lon0=lon0, lat0=lat0, point0=point0, point1=point1, hmin=hmin, hmax=hmax, zmin=zmin, zmax=zmax, tmin=tmin, tmax=tmax, smin=smin, smax=smax, tcontours=tcontours, scontours=scontours, date_string=date_string, fig_name=fig_name)    
示例#2
0
def read_plot_ts_slice (file_path, grid=None, lon0=None, lat0=None, point0=None, point1=None, time_index=None, t_start=None, t_end=None, time_average=False, hmin=None, hmax=None, zmin=None, zmax=None, tmin=None, tmax=None, smin=None, smax=None, tcontours=None, scontours=None, date_string=None, fig_name=None, second_file_path=None):

    grid = choose_grid(grid, file_path)
    check_single_time(time_index, time_average)
    date_string = check_date_string(date_string, file_path, time_index)

    # Inner function to read a variable from the correct NetCDF file and mask appropriately
    def read_and_mask (var_name):
        # Do we need to choose the right file?
        if second_file_path is not None:
            file_path_use = find_variable(file_path, second_file_path, var_name)
        else:
            file_path_use = file_path
        # Read and mask the data
        data = mask_3d(read_netcdf(file_path_use, var_name, time_index=time_index, t_start=t_start, t_end=t_end, time_average=time_average), grid)
        return data

    # Read temperature and salinity
    temp = read_and_mask('THETA')
    salt = read_and_mask('SALT')

    # Plot
    ts_slice_plot(temp, salt, grid, lon0=lon0, lat0=lat0, point0=point0, point1=point1, hmin=hmin, hmax=hmax, zmin=zmin, zmax=zmax, tmin=tmin, tmax=tmax, smin=smin, smax=smax, tcontours=tcontours, scontours=scontours, date_string=date_string, fig_name=fig_name)
示例#3
0
def read_plot_slice_diff (var, file_path_1, file_path_2, grid=None, lon0=None, lat0=None, point0=None, point1=None, time_index=None, t_start=None, t_end=None, time_average=False, time_index_2=None, t_start_2=None, t_end_2=None, hmin=None, hmax=None, zmin=None, zmax=None, vmin=None, vmax=None, contours=None, date_string=None, fig_name=None, eosType='MDJWF', rhoConst=None, Tref=None, Sref=None, tAlpha=None, sBeta=None, ref_depth=0):

    # Figure out if the two files use different time indices
    diff_time = (time_index_2 is not None) or (time_average and (t_start_2 is not None or t_end_2 is not None))

    # Get set up just like read_plot_slice
    grid = choose_grid(grid, file_path_1)
    check_single_time(time_index, time_average)
    date_string = check_date_string(date_string, file_path_1, time_index)

    # Inner function to read a variable from a NetCDF file and mask appropriately
    def read_and_mask (var_name, file_path, check_diff_time=False, gtype='t'):
        if var_name in ['tminustf', 'rho']:
            # Need to read 2 variables
            temp = read_and_mask('THETA', file_path, check_diff_time=check_diff_time)
            salt = read_and_mask('SALT', file_path, check_diff_time=check_diff_time)
            if var_name == 'rho':
                return mask_3d(density(eosType, salt, temp, ref_depth, rhoConst=rhoConst, Tref=Tref, Sref=Sref, tAlpha=tAlpha, sBeta=sBeta), grid)
            elif var_name == 'tminustf':
                return t_minus_tf(temp, salt, grid)
        elif var_name in ['vnorm', 'valong']:
            u = read_and_mask('UVEL', file_path, check_diff_time=check_diff_time, gtype='u')
            v = read_and_mask('VVEL', file_path, check_diff_time=check_diff_time, gtype='v')
            if var_name == 'vnorm':
                return normal_vector(u, v, grid, point0, point1)
            elif var_name == 'valong':
                return parallel_vector(u, v, grid, point0, point1)
        elif var_name == 'tadv_along':
            tadv_x = read_and_mask('ADVx_TH', file_path, check_diff_time=check_diff_time)
            tadv_y = read_and_mask('ADVy_TH', file_path, check_diff_time=check_diff_time)
            return parallel_vector(tadv_x, tadv_y, grid, point0, point1)
        elif var_name == 'tdif_along':
            tdif_x = read_and_mask('DFxE_TH', file_path, check_diff_time=check_diff_time)
            tdif_y = read_and_mask('DFyE_TH', file_path, check_diff_time=check_diff_time)
            return parallel_vector(tdif_x, tdif_y, grid, point0, point1)
        else:
            if check_diff_time and diff_time:
                return mask_3d(read_netcdf(file_path, var_name, time_index=time_index_2, t_start=t_start_2, t_end=t_end_2, time_average=time_average), grid, gtype=gtype)
            else:
                return mask_3d(read_netcdf(file_path, var_name, time_index=time_index, t_start=t_start, t_end=t_end, time_average=time_average), grid, gtype=gtype)

    # Interface to call read_and_mask for each variable
    def read_and_mask_both (var_name, gtype='t'):
        data1 = read_and_mask(var_name, file_path_1, gtype=gtype)
        data2 = read_and_mask(var_name, file_path_2, check_diff_time=True, gtype=gtype)
        return data1, data2

    if var in ['vnorm', 'valong', 'tadv_along', 'tdif_along'] and None in [point0, point1]:
        print 'Error (read_plot_slice_diff): normal or along-transect variables require point0 and point1 to be specified.'
        sys.exit()

    # Read variables and make plots
    if var == 'temp':
        temp_1, temp_2 = read_and_mask_both('THETA')
        slice_plot_diff(temp_1, temp_2, grid, lon0=lon0, lat0=lat0, point0=point0, point1=point1, hmin=hmin, hmax=hmax, zmin=zmin, zmax=zmax, vmin=vmin, vmax=vmax, contours=contours, title='Change in temperature ('+deg_string+'C)', date_string=date_string, fig_name=fig_name)
    elif var == 'salt':
        salt_1, salt_2 = read_and_mask_both('SALT')     
        slice_plot_diff(salt_1, salt_2, grid, lon0=lon0, lat0=lat0, point0=point0, point1=point1, hmin=hmin, hmax=hmax, zmin=zmin, zmax=zmax, vmin=vmin, vmax=vmax, contours=contours, title='Change in salinity (psu)', date_string=date_string, fig_name=fig_name)
    elif var == 'tminustf':
        tmtf_1, tmtf_2 = read_and_mask_both('tminustf')
        slice_plot_diff(tmtf_1, tmtf_2, grid, lon0=lon0, lat0=lat0, point0=point0, point1=point1, hmin=hmin, hmax=hmax, zmin=zmin, zmax=zmax, vmin=vmin, vmax=vmax, contours=contours, title='Change in difference from in-situ freezing point ('+deg_string+')', date_string=date_string, fig_name=fig_name)
    elif var == 'rho':
        rho_1, rho_2 = read_and_mask_both('rho')
        slice_plot_diff(rho_1, rho_2, grid, lon0=lon0, lat0=lat0, point0=point0, point1=point1, hmin=hmin, hmax=hmax, zmin=zmin, zmax=zmax, vmin=vmin, vmax=vmax, contours=contours, title=r'Change in density (kg/m$^3$)', date_string=date_string, fig_name=fig_name)
    elif var == 'u':
        u_1, u_2 = read_and_mask_both('UVEL', gtype='u')
        slice_plot_diff(u_1, u_2, grid, gtype='u', lon0=lon0, lat0=lat0, point0=point0, point1=point1, hmin=hmin, hmax=hmax, zmin=zmin, zmax=zmax, vmin=vmin, vmax=vmax, contours=contours, title='Change in zonal velocity (m/s)', date_string=date_string, fig_name=fig_name)
    elif var == 'v':
        v_1, v_2 = read_and_mask_both('VVEL', gtype='v')
        slice_plot_diff(v_1, v_2, grid, gtype='v', lon0=lon0, lat0=lat0, point0=point0, point1=point1, hmin=hmin, hmax=hmax, zmin=zmin, zmax=zmax, vmin=vmin, vmax=vmax, contours=contours, title='Change in meridional velocity (m/s)', date_string=date_string, fig_name=fig_name)
    elif var == 'vnorm':
        vnorm_1, vnorm_2 = read_and_mask_both(var)
        slice_plot_diff(vnorm_1, vnorm_2, grid, point0=point0, point1=point1, hmin=hmin, hmax=hmax, zmin=zmin, zmax=zmax, vmin=vmin, vmax=vmax, contours=contours, title='Change in normal velocity (m/s)', date_string=date_string, fig_name=fig_name)
    elif var == 'valong':
        valong_1, valong_2 = read_and_mask_both(var)
        slice_plot_diff(valong_1, valong_2, grid, point0=point0, point1=point1, hmin=hmin, hmax=hmax, zmin=zmin, zmax=zmax, vmin=vmin, vmax=vmax, contours=contours, title='Change in along-transect velocity (m/s)', date_string=date_string, fig_name=fig_name)
    elif var == 'tadv_along':
        tadv_along_1, tadv_along_2 = read_and_mask_both(var)
        slice_plot_diff(tadv_along_1, tadv_along_2, grid, point0=point0, point1=point1, hmin=hmin, hmax=hmax, zmin=zmin, zmax=zmax, vmin=vmin, vmax=vmax, contours=contours, title=r'Change in along-transect advective heat transport (Km$^3$/s)', date_string=date_string, fig_name=fig_name)
    elif var == 'tdif_along':
        tdif_along_1, tdif_along_2 = read_and_mask_both(var)
        slice_plot_diff(tdif_along_1, tdif_along_2, grid, point0=point0, point1=point1, hmin=hmin, hmax=hmax, zmin=zmin, zmax=zmax, vmin=vmin, vmax=vmax, contours=contours, title=r'Change in along-transect diffusive heat transport (Km$^3$/s)', date_string=date_string, fig_name=fig_name)
    else:
        print 'Error (read_plot_slice_diff): variable key ' + str(var) + ' does not exist'
        sys.exit()
示例#4
0
def read_plot_slice (var, file_path, grid=None, lon0=None, lat0=None, point0=None, point1=None, time_index=None, t_start=None, t_end=None, time_average=False, hmin=None, hmax=None, zmin=None, zmax=None, vmin=None, vmax=None, contours=None, date_string=None, fig_name=None, second_file_path=None, eosType='MDJWF', rhoConst=None, Tref=None, Sref=None, tAlpha=None, sBeta=None, ref_depth=0):

    # Build the grid if needed
    grid = choose_grid(grid, file_path)
    # Make sure we'll end up with a single record in time
    check_single_time(time_index, time_average)
    # Determine what to write about the date
    date_string = check_date_string(date_string, file_path, time_index)

    # Inner function to read a variable from the correct NetCDF file and mask appropriately
    def read_and_mask (var_name, check_second=False, gtype='t'):
        # Do we need to choose the right file?
        if check_second and second_file_path is not None:
            file_path_use = find_variable(file_path, second_file_path, var_name)
        else:
            file_path_use = file_path
        # Read and mask the data
        return mask_3d(read_netcdf(file_path_use, var_name, time_index=time_index, t_start=t_start, t_end=t_end, time_average=time_average), grid, gtype=gtype)

    # Read necessary variables from NetCDF file and mask appropriately
    if var in ['temp', 'tminustf', 'rho']:
        temp = read_and_mask('THETA', check_second=True)
    if var in ['salt', 'tminustf', 'rho']:
        salt = read_and_mask('SALT', check_second=True)
    if var in ['u', 'vnorm', 'valong']:
        u = read_and_mask('UVEL', gtype='u')
    if var in ['v', 'vnorm', 'valong']:
        v = read_and_mask('VVEL', gtype='v')
    if var == 'tadv_along':
        tadv_x = read_and_mask('ADVx_TH')
        tadv_y = read_and_mask('ADVy_TH')
    if var == 'tdif_along':
        tdif_x = read_and_mask('DFxE_TH')
        tdif_y = read_and_mask('DFyE_TH')

    if var in ['vnorm', 'valong', 'tadv_along', 'tdif_along'] and None in [point0, point1]:
        print 'Error (read_plot_slice): normal or along-transect variables require point0 and point1 to be specified.'
        sys.exit()
            
    # Plot
    if var == 'temp':
        slice_plot(temp, grid, lon0=lon0, lat0=lat0, point0=point0, point1=point1, hmin=hmin, hmax=hmax, zmin=zmin, zmax=zmax, vmin=vmin, vmax=vmax, contours=contours, title='Temperature ('+deg_string+'C)', date_string=date_string, fig_name=fig_name)
    elif var == 'salt':
        slice_plot(salt, grid, lon0=lon0, lat0=lat0, point0=point0, point1=point1, hmin=hmin, hmax=hmax, zmin=zmin, zmax=zmax, vmin=vmin, vmax=vmax, contours=contours, title='Salinity (psu)', date_string=date_string, fig_name=fig_name)
    elif var == 'tminustf':
        slice_plot(t_minus_tf(temp, salt, grid), grid, lon0=lon0, lat0=lat0, point0=point0, point1=point1, hmin=hmin, hmax=hmax, zmin=zmin, zmax=zmax, vmin=vmin, vmax=vmax, contours=contours, ctype='plusminus', title='Difference from in-situ freezing point ('+deg_string+'C)', date_string=date_string, fig_name=fig_name)
    elif var == 'rho':
        # Calculate density
        rho = mask_3d(density(eosType, salt, temp, ref_depth, rhoConst=rhoConst, Tref=Tref, Sref=Sref, tAlpha=tAlpha, sBeta=sBeta), grid)
        slice_plot(rho, grid, lon0=lon0, lat0=lat0, point0=point0, point1=point1, hmin=hmin, hmax=hmax, zmin=zmin, zmax=zmax, vmin=vmin, vmax=vmax, contours=contours, title=r'Density (kg/m$^3$)', date_string=date_string, fig_name=fig_name)
    elif var == 'u':
        slice_plot(u, grid, gtype='u', lon0=lon0, lat0=lat0, point0=point0, point1=point1, hmin=hmin, hmax=hmax, zmin=zmin, zmax=zmax, vmin=vmin, vmax=vmax, ctype='plusminus', contours=contours, title='Zonal velocity (m/s)', date_string=date_string, fig_name=fig_name)
    elif var == 'v':
        slice_plot(v, grid, gtype='v', lon0=lon0, lat0=lat0, point0=point0, point1=point1, hmin=hmin, hmax=hmax, zmin=zmin, zmax=zmax, vmin=vmin, vmax=vmax, ctype='plusminus', contours=contours, title='Meridional velocity (m/s)', date_string=date_string, fig_name=fig_name)
    elif var == 'vnorm':
        vnorm = normal_vector(u, v, grid, point0, point1)
        slice_plot(vnorm, grid, point0=point0, point1=point1, hmin=hmin, hmax=hmax, zmin=zmin, zmax=zmax, vmin=vmin, vmax=vmax, ctype='plusminus', contours=contours, title='Normal velocity (m/s)', date_string=date_string, fig_name=fig_name)
    elif var == 'valong':
        valong = parallel_vector(u, v, grid, point0, point1)
        slice_plot(valong, grid, point0=point0, point1=point1, hmin=hmin, hmax=hmax, zmin=zmin, zmax=zmax, vmin=vmin, vmax=vmax, ctype='plusminus', contours=contours, title='Along-transect velocity (m/s)', date_string=date_string, fig_name=fig_name)
    elif var == 'tadv_along':
        tadv_along = parallel_vector(tadv_x, tadv_y, grid, point0, point1)
        slice_plot(tadv_along, grid, point0=point0, point1=point1, hmin=hmin, hmax=hmax, zmin=zmin, zmax=zmax, vmin=vmin, vmax=vmax, ctype='plusminus', contours=contours, title=r'Along-transect advective heat transport (Km$^3$/s)', date_string=date_string, fig_name=fig_name)
    elif var == 'tdif_along':
        tdif_along = parallel_vector(tdif_x, tdif_y, grid, point0, point1)
        slice_plot(tdif_along, grid, point0=point0, point1=point1, hmin=hmin, hmax=hmax, zmin=zmin, zmax=zmax, vmin=vmin, vmax=vmax, ctype='plusminus', contours=contours, title=r'Along-transect diffusive heat transport (Km$^3$/s)', date_string=date_string, fig_name=fig_name)
    else:
        print 'Error (read_plot_slice): variable key ' + str(var) + ' does not exist'
        sys.exit()
示例#5
0
def ts_distribution_plot(file_path,
                         option='fris',
                         grid=None,
                         time_index=None,
                         t_start=None,
                         t_end=None,
                         time_average=False,
                         second_file_path=None,
                         tmin=None,
                         tmax=None,
                         smin=None,
                         smax=None,
                         num_bins=1000,
                         date_string=None,
                         figsize=(8, 6),
                         fig_name=None):

    # Build the grid if needed
    grid = choose_grid(grid, file_path)
    # Make sure we'll end up with a single record in time
    check_single_time(time_index, time_average)
    # Determine what to write about the date
    date_string = check_date_string(date_string, file_path, time_index)

    # Quick inner function to read data (THETA or SALT)
    def read_data(var_name):
        # First choose the right file
        if second_file_path is not None:
            file_path_use = find_variable(file_path, second_file_path)
        else:
            file_path_use = file_path
        data = read_netcdf(file_path_use,
                           var_name,
                           time_index=time_index,
                           t_start=t_start,
                           t_end=t_end,
                           time_average=time_average)
        return data

    # Call this function for each variable
    temp = read_data('THETA')
    salt = read_data('SALT')

    # Select the points we care about
    if option == 'fris':
        # Select all points in the FRIS cavity
        loc_index = (grid.hfac > 0) * xy_to_xyz(grid.fris_mask, grid)
    elif option == 'cavities':
        # Select all points in ice shelf cavities
        loc_index = (grid.hfac > 0) * xy_to_xyz(grid.ice_mask, grid)
    elif option == 'all':
        # Select all unmasked points
        loc_index = grid.hfac > 0
    else:
        print 'Error (plot_misc): invalid option ' + option
        sys.exit()

    # Inner function to set up bins for a given variable (temp or salt)
    def set_bins(data):
        # Find the bounds on the data at the points we care about
        vmin = np.amin(data[loc_index])
        vmax = np.amax(data[loc_index])
        # Choose a small epsilon to add/subtract from the boundaries
        # This way nothing will be at the edge of a beginning/end bin
        eps = (vmax - vmin) * 1e-3
        # Calculate boundaries of bins
        bins = np.linspace(vmin - eps, vmax + eps, num=num_bins)
        # Now calculate the centres of bins for plotting
        centres = 0.5 * (bins[:-1] + bins[1:])
        return bins, centres

    # Call this function for each variable
    temp_bins, temp_centres = set_bins(temp)
    salt_bins, salt_centres = set_bins(salt)
    # Now set up a 2D array to increment with volume of water masses
    volume = np.zeros([temp_centres.size, salt_centres.size])

    # Loop over all cells to increment volume
    # This can't really be vectorised unfortunately
    for i in range(grid.nx):
        for j in range(grid.ny):
            if option == 'fris' and not grid.fris_mask[j, i]:
                # Disregard all points not in FRIS cavity
                continue
            if option == 'cavities' and not grid.ice_mask[j, i]:
                # Disregard all points not in ice shelf cavities
                continue
            for k in range(grid.nz):
                if grid.hfac[k, j, i] == 0:
                    # Disregard all masked points
                    continue
                # If we're still here, it's a point we care about
                # Figure out which bins it falls into
                temp_index = np.nonzero(temp_bins > temp[k, j, i])[0][0] - 1
                salt_index = np.nonzero(salt_bins > salt[k, j, i])[0][0] - 1
                # Increment volume array
                volume[temp_index, salt_index] += grid.dV[k, j, i]
    # Mask bins with zero volume
    volume = np.ma.masked_where(volume == 0, volume)

    # Find the volume bounds for plotting
    min_vol = np.log(np.amin(volume))
    max_vol = np.log(np.amax(volume))
    # Calculate the surface freezing point for plotting
    tfreeze_sfc = tfreeze(salt_centres, 0)
    # Choose the plotting bounds if not set
    if tmin is None:
        tmin = temp_bins[0]
    if tmax is None:
        tmax = temp_bins[-1]
    if smin is None:
        smin = salt_bins[0]
    if smax is None:
        smax = salt_bins[-1]
    # Construct the title
    title = 'Water masses'
    if option == 'fris':
        title += ' in FRIS cavity'
    elif option == 'cavities':
        title += ' in ice shelf cavities'
    if date_string != '':
        title += ', ' + date_string

    # Plot
    fig, ax = plt.subplots(figsize=figsize)
    # Use a log scale for visibility
    img = plt.pcolor(salt_centres,
                     temp_centres,
                     np.log(volume),
                     vmin=min_vol,
                     vmax=max_vol)
    # Add the surface freezing point
    plt.plot(salt_centres,
             tfreeze_sfc,
             color='black',
             linestyle='dashed',
             linewidth=2)
    ax.grid(True)
    ax.set_xlim([smin, smax])
    ax.set_ylim([tmin, tmax])
    plt.xlabel('Salinity (psu)')
    plt.ylabel('Temperature (' + deg_string + 'C)')
    plt.colorbar(img)
    plt.text(.9,
             .6,
             'log of volume',
             ha='center',
             rotation=-90,
             transform=fig.transFigure)
    plt.title(title)
    finished_plot(fig, fig_name=fig_name)
示例#6
0
def ts_distribution_plot(file_path,
                         region='all',
                         grid=None,
                         time_index=None,
                         t_start=None,
                         t_end=None,
                         time_average=False,
                         second_file_path=None,
                         tmin=None,
                         tmax=None,
                         smin=None,
                         smax=None,
                         num_bins=1000,
                         date_string=None,
                         figsize=(8, 6),
                         fig_name=None):

    # Build the grid if needed
    grid = choose_grid(grid, file_path)
    # Make sure we'll end up with a single record in time
    check_single_time(time_index, time_average)
    # Determine what to write about the date
    date_string = check_date_string(date_string, file_path, time_index)

    # Quick inner function to read data (THETA or SALT)
    def read_data(var_name):
        # First choose the right file
        if second_file_path is not None:
            file_path_use = find_variable(file_path, second_file_path)
        else:
            file_path_use = file_path
        data = read_netcdf(file_path_use,
                           var_name,
                           time_index=time_index,
                           t_start=t_start,
                           t_end=t_end,
                           time_average=time_average)
        return data

    # Call this function for each variable
    temp = read_data('THETA')
    salt = read_data('SALT')

    if region == 'all':
        mask = grid.hfac > 0
    elif region == 'cavities':
        mask = grid.ice_mask
    else:
        mask = grid.get_region_mask(region)

    # Make the bins
    volume, temp_centres, salt_centres, temp_edges, salt_edges = ts_binning(
        temp, salt, grid, mask, num_bins=num_bins)

    # Find the volume bounds for plotting
    min_vol = np.log(np.amin(volume))
    max_vol = np.log(np.amax(volume))
    # Calculate the surface freezing point for plotting
    tfreeze_sfc = tfreeze(salt_centres, 0)
    # Choose the plotting bounds if not set
    if tmin is None:
        tmin = temp_bins[0]
    if tmax is None:
        tmax = temp_bins[-1]
    if smin is None:
        smin = salt_bins[0]
    if smax is None:
        smax = salt_bins[-1]
    # Construct the title
    title = 'Water masses'
    if region == 'all':
        pass
    elif region == 'cavities':
        title += ' in ice shelf cavities'
    elif region.endswith('cavity'):
        title += ' in ' + region_names[region[:region.index('_cavity')]]
    else:
        title += ' in ' + region_names[region]
    if date_string != '':
        title += ', ' + date_string

    # Plot
    fig, ax = plt.subplots(figsize=figsize)
    # Use a log scale for visibility
    img = plt.pcolor(salt_centres,
                     temp_centres,
                     np.log(volume),
                     vmin=min_vol,
                     vmax=max_vol)
    # Add the surface freezing point
    plt.plot(salt_centres,
             tfreeze_sfc,
             color='black',
             linestyle='dashed',
             linewidth=2)
    ax.grid(True)
    ax.set_xlim([smin, smax])
    ax.set_ylim([tmin, tmax])
    plt.xlabel('Salinity (psu)')
    plt.ylabel('Temperature (' + deg_string + 'C)')
    plt.colorbar(img)
    plt.text(.9,
             .6,
             'log of volume',
             ha='center',
             rotation=-90,
             transform=fig.transFigure)
    plt.title(title)
    finished_plot(fig, fig_name=fig_name)