From 7b28dcfbf52ff7a28b0e107bd70654f6c3ecc857 Mon Sep 17 00:00:00 2001 From: thopri <thopri@144-148.noc.soton.ac.uk> Date: Fri, 6 Dec 2019 14:20:32 +0000 Subject: [PATCH] added boundary plot script to trunk --- .gitignore | 4 +- test_scripts/__init__.py | 0 test_scripts/bdy_var_plot.py | 259 +++++++++++++++++++++++++++++++++++ 3 files changed, 262 insertions(+), 1 deletion(-) create mode 100644 test_scripts/__init__.py create mode 100644 test_scripts/bdy_var_plot.py diff --git a/.gitignore b/.gitignore index 37d979a..d4b7d99 100644 --- a/.gitignore +++ b/.gitignore @@ -5,7 +5,8 @@ __pycache__/ # C extensions *.so - +*.nc +*.xml # Distribution / packaging .Python build/ @@ -26,6 +27,7 @@ wheels/ *.egg MANIFEST outputs +.DS_Store # PyInstaller # Usually these files are written by a python script from a template diff --git a/test_scripts/__init__.py b/test_scripts/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test_scripts/bdy_var_plot.py b/test_scripts/bdy_var_plot.py new file mode 100644 index 0000000..cfc4730 --- /dev/null +++ b/test_scripts/bdy_var_plot.py @@ -0,0 +1,259 @@ +#!/usr/bin/env python2 +# -*- coding: utf-8 -*- + +import numpy as np +import scipy.spatial as sp +from netCDF4 import Dataset +import matplotlib.pyplot as plt +from matplotlib.collections import PatchCollection +from matplotlib.patches import Polygon + + +def nemo_bdy_order(fname): + """ + Determine the ordering and breaks in BDY files to aid plotting. + This function takes the i/j coordinates from BDY files and orders them sequentially + making it easier to visualise sections along the open boundary. Breaks in the open + boundary are also determined (i.e. where the distance between two points > 2**0.5) + Args: + fname (str) : filename of BDY file + Returns: + bdy_ind (dict): re-ordered indices + bdy_dst (dict): distance (in model coords) between points + bdy_brk (dict): location of the break points in the open boundary + """ + + # open file pointer and extract data + rootgrp = Dataset(fname, "r", format="NETCDF4") + nbi = np.squeeze(rootgrp.variables['nbidta'][:, :]) - 1 # subtract 1 for python indexing + nbj = np.squeeze(rootgrp.variables['nbjdta'][:, :]) - 1 + nbr = np.squeeze(rootgrp.variables['nbrdta'][:, :]) - 1 + lon = np.squeeze(rootgrp.variables['nav_lon'][:, :]) + lat = np.squeeze(rootgrp.variables['nav_lat'][:, :]) + rootgrp.close() + + rw = np.amax(nbr) + 1 + bdy_ind = {} + bdy_brk = {} + bdy_dst = {} + nbdy = [] + + for r in range(rw): + nbdy.append(np.sum(nbr == r)) + + # TODO: deal with domain that spans wrap + + # start with outer rim and work in + + for r in range(rw): + + # set initial constants + + ind = nbr == r + nbi_tmp = nbi[ind] + nbj_tmp = nbj[ind] + + count = 1 + id_order = np.zeros((nbdy[r], 1), dtype=int) - 1 + id_order[0,] = 0 + flag = False + mark = 0 + source_tree = sp.cKDTree(zip(nbi_tmp, nbj_tmp), balanced_tree=False, compact_nodes=False) + + # order bdy entries + + while count < nbdy[r]: + + nn_dist, nn_id = source_tree.query(zip(nbi_tmp[id_order[count - 1]], nbj_tmp[id_order[count - 1]]), + k=3, distance_upper_bound=2.9) + if np.sum(id_order == nn_id[0, 1]) == 1: # is the nearest point already in the list? + if np.sum(id_order == nn_id[0, 2]) == 1: # is the 2nd nearest point already in the list? + if flag == False: # then we've found an end point and we can start the search in earnest! + + flag = True + id_order[mark] = id_order[count - 1] # reset values + id_order[mark + 1:] = -1 # reset values + count = mark + 1 # reset counter + else: + i = 0 # should this be zero? + t = count + while count == t: + if np.sum(id_order == i) == 1: + i += 1 + else: + id_order[count] = i + flag = False + mark = count + count += 1 + elif nn_id[0, 2] == nbdy[r]: + i = 0 + t = count + while count == t: + if np.sum(id_order == i) == 1: + i += 1 + else: + id_order[count] = i + flag = False + mark = count + count += 1 + else: + id_order[count] = nn_id[0, 2] + count += 1 + else: + id_order[count] = nn_id[0, 1] + count += 1 + + bdy_ind[r] = id_order + bdy_dst[r] = np.sqrt((np.diff(np.hstack((nbi_tmp[id_order], nbj_tmp[id_order])), axis=0) ** 2).sum(axis=1)) + bdy_brk[r], = np.where(bdy_dst[r] > 2 ** 0.5) + bdy_brk[r] += 1 + bdy_brk[r] = np.insert(bdy_brk[r], 0, 0) # insert start point + bdy_brk[r] = np.insert(bdy_brk[r], len(bdy_brk[r]), len(id_order)) # insert end point + bdy_dst[r] = np.insert(np.cumsum(bdy_dst[r]), 0, 0) + + if r == 2: # change to a valid rw number to get a visual output (outer most rw is zero) + f, ax = plt.subplots(nrows=1, ncols=1, figsize=(10, 10)) + plt.scatter(nbi_tmp[bdy_ind[r][:]], nbj_tmp[bdy_ind[r][:]], s=1, marker='.') + for t in np.arange(0, len(bdy_ind[r]), 20): + plt.text(nbi_tmp[bdy_ind[r][t]], nbj_tmp[bdy_ind[r][t]], t) + + return bdy_ind, bdy_dst, bdy_brk + + +def plot_bdy(fname, bdy_ind, bdy_dst, bdy_brk, varnam, t, rw): + """ + Determine the ordering and breaks in BDY files to aid plotting. + This function takes the i/j coordinates from BDY files and orders them sequentially + making it easier to visualise sections along the open boundary. Breaks in the open + boundary are also determined (i.e. where the distance between two points > 2**0.5) + Args: + fname (str) : filename of BDY file + Returns: + bdy_ind (dict): re-ordered indices + bdy_dst (dict): distance (in model coords) between points + bdy_brk (dict): location of the break points in the open boundary + """ + + # need to write in a check that either i or j are single values + + rootgrp = Dataset(fname, "r", format="NETCDF4") + var = np.squeeze(rootgrp.variables[varnam][t, :]) + nbr = np.squeeze(rootgrp.variables['nbrdta'][:, :]) - 1 + var = var[:, nbr == rw] + + # let us use gdept as the central depth irrespective of whether t, u or v + + try: + gdep = np.squeeze(rootgrp.variables['deptht'][:, :, :]) + except KeyError: + try: + gdep = np.squeeze(rootgrp.variables['gdept'][:, :, :]) + except KeyError: + try: + gdep = np.squeeze(rootgrp.variables['depthu'][:, :, :]) + except KeyError: + try: + gdep = np.squeeze(rootgrp.variables['depthv'][:, :, :]) + except KeyError: + print 'depth variable not found' + + rootgrp.close() + + jpk = len(gdep[:, 0]) + nsc = len(bdy_brk[rw][:]) - 1 + + dep = {} + dta = {} + + # divide data up into sections and re-order + + for n in range(nsc): + dta[n] = np.squeeze(var[:, bdy_ind[rw][bdy_brk[rw][n]:bdy_brk[rw][n + 1]]]) + dep[n] = np.squeeze(gdep[:, bdy_ind[rw][bdy_brk[rw][n]:bdy_brk[rw][n + 1]]]) + + # loop over number of sections and plot + + f, ax = plt.subplots(nrows=1, ncols=1, figsize=(11, 4)) + ax.plot(dta[0][10, :]) + ax.set_title('BDY points along section: 1, depth level: 10') + + f, ax = plt.subplots(nrows=nsc, ncols=1, figsize=(14, 10 * nsc)) + + for n in range(nsc): + print('NSC '+str(n)) + plt.sca(ax[n]) + + # from gdep create some pseudo w points + + gdept = dep[n][:, :] + coords = np.arange(0, len(gdept[0, :])) + gdepw = np.zeros((len(gdept[:, 0]) + 1, len(gdept[0, :]))) + for z in range(jpk): + gdepw[z + 1, :] = gdept[z, :] + (gdept[z, :] - gdepw[z, :]) + + gdepvw = np.zeros((len(gdept[:, 0]) + 1, len(gdept[0, :]) + 1)) + + # TODO: put in an adjustment for zps levels + + gdepvw[:, 1:-1] = (gdepw[:, :-1] + gdepw[:, 1:]) / 2 + gdepvw[:, 0] = gdepvw[:, 1] + gdepvw[:, -1] = gdepvw[:, -2] + + # create a pseudo bathymetry from the depth data + + bathy = np.zeros_like(coords) + mbath = np.sum(dta[n].mask == 0, axis=0) + + for i in range(len(coords)): + bathy[i] = gdepw[mbath[i], i] + + bathy_patch = Polygon(np.vstack((np.hstack((coords[0], coords, coords[-1])), + np.hstack((np.amax(bathy[:]), bathy, np.amax(bathy[:]))))).T, + closed=True, + facecolor=(0.8, 0.8, 0), alpha=0, edgecolor=None) + + # Add patch to axes + ax[n].add_patch(bathy_patch) + ax[n].set_title('BDY points along section: ' + str(n)) + patches = [] + colors = [] + + for i in range(len(coords)): + + for k in np.arange(jpk - 2, -1, -1): + + if dta[n][k, i] > -10: + x = [coords[i] - 0.5, coords[i], coords[i] + 0.5, + coords[i] + 0.5, coords[i], coords[i] - 0.5, coords[i] - 0.5] + y = [gdepvw[k + 1, i], gdepw[k + 1, i], gdepvw[k + 1, i + 1], + gdepvw[k, i + 1], gdepw[k, i], gdepvw[k, i], gdepvw[k + 1, i]] + + polygon = Polygon(np.vstack((x, y)).T, True) + patches.append(polygon) + colors = np.append(colors, dta[n][k, i]) + + # for i in range(len(coords)): + # #print(i) + # for k in np.arange(jpk - 2, -1, -1): + # x = [coords[i] - 0.5, coords[i], coords[i] + 0.5, + # coords[i] + 0.5, coords[i], coords[i] - 0.5, coords[i] - 0.5] + # y = [gdepvw[k + 1, i], gdepw[k + 1, i], gdepvw[k + 1, i + 1], + # gdepvw[k, i + 1], gdepw[k, i], gdepvw[k, i], gdepvw[k + 1, i]] + # plt.plot(x, y, 'k-', linewidth=0.1) + # plt.plot(coords[i], gdept[k, i], 'k.', markersize=1) + + plt.plot(coords, bathy, '-', color=(0.4, 0, 0)) + p = PatchCollection(patches, alpha=0.8, edgecolor='none') + p.set_array(np.array(colors)) + ax[n].add_collection(p) + f.colorbar(p, ax=ax[n]) + ax[n].set_ylim((0, np.max(bathy))) + ax[n].invert_yaxis() + + return f + +fname = '/Users/thopri/Projects/PyNEMO/outputs/NNA_R12_bdyT_y1979m11.nc' +ind, dst, brk = nemo_bdy_order(fname) +f = plot_bdy(fname, ind, dst, brk, 'votemper', 0, 0) + +plt.show() \ No newline at end of file -- GitLab