diff --git a/unst_msh_gen/regional/InterpolateBathymetry/AddBathyToMesh.py b/unst_msh_gen/regional/InterpolateBathymetry/AddBathyToMesh.py new file mode 100644 index 0000000..89c6704 --- /dev/null +++ b/unst_msh_gen/regional/InterpolateBathymetry/AddBathyToMesh.py @@ -0,0 +1,37 @@ + +# +# Function for taking bathymetry field in a text file and replacing bathymetry +# from a WW3 .msh files with the values from the file specified on the command +# line: +# +# $python3 AddBathyToMesh.py MyBathy.txt +# +# creates a new WW3 mesh with bathymetry from MyBathy.txt +# +# + +import FiniteElementMeshRoutines as FE +import numpy as np +import sys + +mshnm="HawaiiTest" +mesh="meshes/"+mshnm+".msh" + +flin=sys.argv[1] +flout=flin+".WW3.msh" + +x, y, z0, e, bnd = FE.loadWW3Mesh(mesh) + +zmin=1. +nn=len(x) +z=np.zeros(nn) +f=open(flin, 'r') +for k in range(nn): + line=f.readline() + #print(str(k)+" "+line) + z[k]=float(line.strip()) + z[k]=-z[k] + z[k]=max(z[k],zmin) +f.close +FE.WriteWW3Mesh(flout,x,y,z,e,bnd) + diff --git a/unst_msh_gen/regional/InterpolateBathymetry/DivideMeshNodes.py b/unst_msh_gen/regional/InterpolateBathymetry/DivideMeshNodes.py new file mode 100644 index 0000000..48ceecd --- /dev/null +++ b/unst_msh_gen/regional/InterpolateBathymetry/DivideMeshNodes.py @@ -0,0 +1,141 @@ + +# +# This script partitions the nodes in mshnm into NP parts and writes a script to +# interpolate bathymetry data to the nodes in NP parallel parts +# call: +# +# $ python3 DivideMeshNodes.py 100 +# +# to create jobcard: jobcardInterpBathy2Mesh, which is then run: +# +# $ sbatch jobcardInterpBathy2Mesh +# +# to interpolate the bathymetry in 100 parallel tasks +# +# This script should be run after the bathymetry data sets specified in +# InterpolateCRM.partscat.py are present +# +""" +Ursa environment + +module purge +module use /scratch3/NCEPDEV/climate/Keston.Smith/global-workflowA/sorc/ufs_model.fd/modulefiles +module load ufs_ursa.intel +module load py-scipy/1.14.1 +module load py-netcdf4/1.7.1.post2 +pip list +""" + +import os +import argparse +import time +import numpy as np +import netCDF4 as nc +import math +import sys +from scipy.interpolate import RegularGridInterpolator +#import re +import FiniteElementMeshRoutines as FE +import random + +mshnm="HawaiiTest" +mesh="meshes/"+mshnm+".msh" +OutDir=mshnm+".files/" + +jobcard="jobcardInterpBathy2Mesh" + + +def WriteInterpJobscript(fl,N, ComputeNodes): + with open(fl, 'w') as f: + #yi[k]=float(values[4]) + f.write("#!/bin/bash \n") + f.write("#SBATCH --job-name=CRM_interp_masterscript \n") +# f.write("#SBATCH --ntasks="+str(N)+" \n") + f.write("#SBATCH --ntasks=1 \n") # ntasks per interpolation + f.write("#SBATCH --time=08:00:00 \n") + f.write("#SBATCH --output=mpi_test_%j.log \n") + f.write("#SBATCH --error=%j.err \n") + f.write("#SBATCH --account=marine-cpu \n") + f.write("#SBATCH --nodes="+str(ComputeNodes)+" \n") + f.write("#SBATCH --ntasks-per-core=1"+" \n") + f.write("#SBATCH --array=0-"+str(N-1)+" \n") + + f.write(" \n") + + f.write("module purge \n") + f.write("module use /scratch3/NCEPDEV/climate/Keston.Smith/global-workflowA/sorc/ufs_model.fd/modulefiles \n") + f.write("module load ufs_ursa.intel \n") + f.write("module load py-scipy/1.14.1 \n") + f.write("module load py-netcdf4/1.7.1.post2 \n") + f.write("pip list \n") + f.write("srun python3 InterpolateCRM.partscat.py $SLURM_ARRAY_TASK_ID > InterpJob.$SLURM_ARRAY_TASK_ID.out \n") + f.write("wait \n") + f.write("## Run this after the full job array is compleate.\n") + f.write("## Patch randomly shuffled fields in orgigonal order.\n") + + f.write("python3 KnitOutputBackTogether.py "+str(N)+" GMN\n") + f.write("python3 KnitOutputBackTogether.py "+str(N)+" GMU\n") + f.write("python3 KnitOutputBackTogether.py "+str(N)+" InvDist\n") + f.write("python3 KnitOutputBackTogether.py "+str(N)+" NDataPoints\n") + f.write("python3 KnitOutputBackTogether.py "+str(N)+" ClosestValue\n") + f.write("python3 KnitOutputBackTogether.py "+str(N)+" LengthScale\n") + f.write("python3 KnitOutputBackTogether.py "+str(N)+" GMU.std\n") + f.write("python3 KnitOutputBackTogether.py "+str(N)+" GMN.std\n") + #f.write("python3 KnitOutputBackTogether.py "+str(N)+" GMM\n") + #f.write("python3 KnitOutputBackTogether.py "+str(N)+" GM0\n") + + +# Create the output directory------------------------------------------ +try: + os.mkdir(OutDir) + print(f"Directory '{OutDir}' created successfully.") +except FileExistsError: + print(f"Directory '{OutDir}' already exists. Proceeding ...") +except PermissionError: + print(f"Permission denied: Unable to create '{OutDir}'.") + +fl="meshes/"+mshnm+".msh" + +xi, yi, ei =FE.loadWW3MeshCoords(fl) + +#tmp +lsE=FE.lengthscale(xi, yi, ei) +areaE=FE.ElementArea(xi, yi, ei) +lsN=FE.ComputeNodeLengthScale(lsE, areaE, ei) +np.savetxt("LengthScaleNodes.txt", lsN, fmt='%.6f', delimiter='\n') + +# randomly sort nodes for roughly equal jobs size +# nodes within CRM envelope use far more compute than +# open ocean nodes with fewer points used in interpolation + +nn=len(xi) +Nodes = list(range( nn )) +random.shuffle(Nodes) # randomize order of nodes for equitable job load + +Nparts=int(sys.argv[1]) + +nn=xi.shape[0] +NodesPerProc=math.ceil(nn/Nparts) +print(str(NodesPerProc)+ " " + str(nn)) +a = range(nn) + +NodeList = [] +for i in range(0, len(a), NodesPerProc): # Slice list in steps of n + NodeList.append(a[i:i + NodesPerProc]) + +print("NodeList") +print(NodeList) +for k in range(Nparts): + flo=OutDir+'NodeList.'+str(k)+'.txt' + f=open(flo, 'w') + f.write("List of nodes for job "+str(k) + "\n") + List=NodeList[k] + n=len(List) + f.write(str(n) + "\n") + for j in range(n): + #f.write(str(List[j]) + '\n') # sequential order of nodes, output can be cat > back together + f.write(str(Nodes[List[j]]) + '\n') # random order of nodes + f.close + +#write jobcard to do interpolation +WriteInterpJobscript(jobcard,Nparts,1) diff --git a/unst_msh_gen/regional/InterpolateBathymetry/DowloadBathyData.sh b/unst_msh_gen/regional/InterpolateBathymetry/DowloadBathyData.sh new file mode 100755 index 0000000..0355532 --- /dev/null +++ b/unst_msh_gen/regional/InterpolateBathymetry/DowloadBathyData.sh @@ -0,0 +1,103 @@ +#!/bin/bash +############################################################################################################# + +# Script to download global and regional bathymetry files: +# You will need ~ 20 GB free space + +echo "Downloading bathymetry data" + +############################################################################################################# +######################## Download main Coastal Relief Moesl (CRM) files ##################################### +############################################################################################################# +# see: https://www.ncei.noaa.gov/products/coastal-relief-model +############################################################################################################# +#Northeast Atlantic +wget --output-document crm_vol1_2023.nc https://www.ngdc.noaa.gov/thredds/fileServer/crm/cudem/crm_vol1_2023.nc +#Southeast Atlantic +wget --output-document crm_vol2_2023.nc https://www.ngdc.noaa.gov/thredds/fileServer/crm/cudem/crm_vol2_2023.nc +# Florida and East Gulf of America +wget --output-document crm_vol3_2023.nc https://www.ngdc.noaa.gov/thredds/fileServer/crm/cudem/crm_vol3_2023.nc +# Central GOA +wget --output-document crm_vol4_2023.nc https://www.ngdc.noaa.gov/thredds/fileServer/crm/cudem/crm_vol4_2023.nc +# Western GOA +wget --output-document crm_vol5_2023.nc https://www.ngdc.noaa.gov/thredds/fileServer/crm/cudem/crm_vol5_2023.nc +#Central Pacific +wget --output-document crm_vol7_2023.nc https://www.ngdc.noaa.gov/thredds/fileServer/crm/cudem/crm_vol7_2023.nc +#Northwest Pacific +wget --output-document crm_vol8_2023.nc https://www.ngdc.noaa.gov/thredds/fileServer/crm/cudem/crm_vol8_2023.nc +#Puerto Rico +wget --output-document crm_vol9_2023.nc https://www.ngdc.noaa.gov/thredds/fileServer/crm/cudem/crm_vol9_2023.nc +#Hawaii +wget --output-document crm_vol10_2023.nc https://www.ngdc.noaa.gov/thredds/fileServer/crm/cudem/crm_vol10_2023.nc +#Other CRM files +# Southern California +wget --output-document crm_vol6.nc https://www.ngdc.noaa.gov/thredds/fileServer/crm/crm_vol6.nc +# Southern Alaska +wget --output-document crm_southak.nc https://www.ngdc.noaa.gov/thredds/fileServer/crm/crm_southak.nc +############################################################################################################# + +echo "Done Coastal Relief Model (CRM) Download" + +############################################################################################################# +######################## Download regional files in Pacific ################################################# +############################################################################################################# +# see: https://pae-paha.pacioos.hawaii.edu/thredds/bathymetry.html +############################################################################################################# +# American Samoa +wget --output-document ngdc_bathy_90m_amsamoa.nc https://pae-paha.pacioos.hawaii.edu/thredds/ncss/ngdc_bathy_90m_amsamoa?var=elev&disableLLSubset=on&disableProjSubset=on&horizStride=1&addLatLon=true +# Mariana islands +wget --output-document ngdc_bathy_180m_mariana.nc https://pae-paha.pacioos.hawaii.edu/thredds/ncss/ngdc_bathy_180m_mariana?var=elev&disableLLSubset=on&disableProjSubset=on&horizStride=1&addLatLon=true +#North Western Hawiian islands +wget --output-document hurl_bathy_60m_nwhi.nc https://pae-paha.pacioos.hawaii.edu/thredds/ncss/hurl_bathy_60m_nwhi?var=elev&disableLLSubset=on&disableProjSubset=on&horizStride=1&addLatLon=true +############################################################################################################# + +echo "Done regional files in Pacific Download" + +############################################################################################################# +######################## Download small scale bathymetry for US Minor outlying islands in Pacific ########### +############################################################################################################# +# see: https://pae-paha.pacioos.hawaii.edu/thredds/bathymetry.html +############################################################################################################# +wget --output-document ngdc_bathy_10m_wake.nc https://pae-paha.pacioos.hawaii.edu/thredds/ncss/ngdc_bathy_10m_wake?var=elev&disableLLSubset=on&disableProjSubset=on&horizStride=1&addLatLon=true + +wget --output-document pibhmc_bathy_20m_jarvis.nc https://pae-paha.pacioos.hawaii.edu/thredds/ncss/pibhmc_bathy_20m_jarvis?var=elev&disableLLSubset=on&disableProjSubset=on&horizStride=1&addLatLon=true + +wget --output-document pibhmc_bathy_20m_johnston.nc https://pae-paha.pacioos.hawaii.edu/thredds/ncss/pibhmc_bathy_20m_johnston?var=elev&disableLLSubset=on&disableProjSubset=on&horizStride=1&addLatLon=true + +wget --output-document pibhmc_bathy_20m_kingman.nc https://pae-paha.pacioos.hawaii.edu/thredds/ncss/pibhmc_bathy_20m_kingman?var=elev&disableLLSubset=on&disableProjSubset=on&horizStride=1&addLatLon=true + +wget --output-document pibhmc_bathy_40m_baker.nc https://pae-paha.pacioos.hawaii.edu/thredds/ncss/pibhmc_bathy_40m_baker?var=elev&disableLLSubset=on&disableProjSubset=on&horizStride=1&addLatLon=true + +wget --output-document pibhmc_bathy_40m_howland.nc https://pae-paha.pacioos.hawaii.edu/thredds/ncss/pibhmc_bathy_40m_howland?var=elev&disableLLSubset=on&disableProjSubset=on&horizStride=1&addLatLon=true + +wget --output-document pibhmc_bathy_40m_palmyra.nc https://pae-paha.pacioos.hawaii.edu/thredds/ncss/pibhmc_bathy_40m_palmyra?var=elev&disableLLSubset=on&disableProjSubset=on&horizStride=1&addLatLon=true + +wget --output-document pibhmc_bathy_40m_rose.nc https://pae-paha.pacioos.hawaii.edu/thredds/ncss/pibhmc_bathy_40m_rose?var=elev&disableLLSubset=on&disableProjSubset=on&horizStride=1&addLatLon=true + +wget --output-document pibhmc_bathy_40m_vailuluu.nc https://pae-paha.pacioos.hawaii.edu/thredds/ncss/pibhmc_bathy_40m_vailuluu?var=elev&disableLLSubset=on&disableProjSubset=on&horizStride=1&addLatLon=true + +wget --output-document pibhmc_bathy_40m_swains.nc https://pae-paha.pacioos.hawaii.edu/thredds/ncss/pibhmc_bathy_40m_swains?var=elev&disableLLSubset=on&disableProjSubset=on&horizStride=1&addLatLon=true + +wget --output-document pibhmc_bathy_5m_palmyra.nc https://pae-paha.pacioos.hawaii.edu/thredds/ncss/pibhmc_bathy_5m_palmyra?var=elev&disableLLSubset=on&disableProjSubset=on&horizStride=1&addLatLon=true + +wget --output-document sopac_bathy_50m_majuro_reef.nc https://pae-paha.pacioos.hawaii.edu/thredds/ncss/sopac_bathy_50m_majuro_reef?var=elev&disableLLSubset=on&disableProjSubset=on&horizStride=1&addLatLon=true +############################################################################################################# + +echo "Done small scale bathymetry for US Minor outlying islands in Pacific download" + +############################################################################################################# +######################## Global SDB [~0m to ~30m] depth from copernicus ##################################### +############################################################################################################# +# see: https://data.marine.copernicus.eu/product/BATHYMETRY_GLO_PHY_COASTAL_L4_MY_016_001/files?subdataset=cmems_obs-sdb_glo_phy_comp_my_100m-l4-s2_static_202511 +############################################################################################################# +wget --output-document cmems_obs-sdb_glo_phy-comp_my-oa-100m-l4-s2_static.nc https://s3.waw3-1.cloudferro.com/mdl-native-17/native/BATHYMETRY_GLO_PHY_COASTAL_L4_MY_016_001/cmems_obs-sdb_glo_phy_wk_my_100m-l4-s2_static_202511/cmems_obs-sdb_glo_phy-wk_my-oa-100m-l4-s2_static.nc +############################################################################################################# + +echo "Done Global SDB download" + +############################################################################################################# +######################## 60 second Global Bathymetry######################################################### +wget https://github.com/dengwirda/dem/releases/download/v0.1.1/RTopo_2_0_4_GEBCO_v2024_60sec_pixel.zip +############################################################################################################# + +echo "Done Global Bathymetry download" diff --git a/unst_msh_gen/regional/InterpolateBathymetry/FiniteElementMeshRoutines.py b/unst_msh_gen/regional/InterpolateBathymetry/FiniteElementMeshRoutines.py new file mode 100644 index 0000000..993760e --- /dev/null +++ b/unst_msh_gen/regional/InterpolateBathymetry/FiniteElementMeshRoutines.py @@ -0,0 +1,228 @@ +# +# Functions for reading and writing WW3 meshes, calculating element +# area, and estimateing mesh length scale. +# +# x and y are length nn node coordinates +# e is a (Number-of-Elements by 3) element array +# bnd is a length nb list of open ocean boundary nodes +# Note:(nodes are assumed to be indexed from 1,2,...,nn within e +# and bnd) +# +# loadWW3MeshCoords(fl): read mesh nodes and elements from file +# +# loadWW3Mesh(fl): read mesh nodes, elements, and open boundary nodes +# from WW3 .msh file +# +# WriteWW3Mesh(flo,x,y,z,e,bnd): write WW3 .msh file +# +# lengthscale(x, y, e): compute length scale for each element +# +# ElementArea(x, y, e): compute area for each element +# +# ComputeNodeLengthScale(x,y,e): estimate length scale at each node +# + +import numpy as np +import math +#from geopy import distance +import re + +# Simple distance approximation in kilometers for calculating element +# area and lengthscale. +deg2km=111.132954 +deg2rad=np.pi/180. +def Distance(x0,y0,x1,y1): + d=np.sqrt( + ( deg2km*np.cos(deg2rad*(y0+y1)/2 )*(x1-x0) )**2 + + ( deg2km*(y1-y0))**2 ) + return d + +def loadWW3MeshCoords(fl): + f=open(fl, 'r') + header = f.readline() + header = f.readline() + header = f.readline() + header = f.readline() + header = f.readline() # number of nodes + nn = re.findall(r'\d+', header) + nn=int(nn[0]) + print(nn) + xi=np.zeros(nn) + yi=np.zeros(nn) + k=0 + for i in range(nn): + A = f.readline() + values = A.split(" ") + if len(values)>4: + xi[k]=values[2] + yi[k]=values[4] + else: + xi[k]=values[1] + yi[k]=values[2] + k=k+1 + print("number of nodes read: "+str(k)) + header = f.readline() + header = f.readline() + header = f.readline() # number of elements + ne=int(header) + print("ne=",str(ne)) + ei=np.zeros((ne,3), dtype=int) + k=0 + for i in range(ne): + A = f.readline() + values = A.split(" ") + #need to factor bnd nodes + if len(values)>15: + ei[k,0]=int(values[12]) + ei[k,1]=int(values[14]) + ei[k,2]=int(values[16]) + k=k+1 + elif len(values)>7: + ei[k,0]=int(values[6]) + ei[k,1]=int(values[7]) + ei[k,2]=int(values[8]) + k=k+1 + print("number of elements read: "+str(k)) + return xi, yi, ei + + +def loadWW3Mesh(fl): + f=open(fl, 'r') + header = f.readline() + header = f.readline() + header = f.readline() + header = f.readline() + header = f.readline() # number of nodes + nn=int(header) + print("nn = "+str(nn)) + xi=np.zeros(nn) + yi=np.zeros(nn) + zi=np.zeros(nn) + k=0 + for i in range(nn): + A = f.readline() + values = A.split(" ") + if len(values)>4: + xi[k]=values[2] + yi[k]=values[4] + zi[k]=values[6] + else: + xi[k]=values[1] + yi[k]=values[2] + zi[k]=values[3] + k=k+1 + print("number of nodes read: "+str(k)) + header = f.readline() + header = f.readline() + header = f.readline() # number of elements + ne=int(header)#includes boundary nodes and actual elements + print("ne="+str(ne)+" -includes boundary nodes") + nbnd=0 + bnd=[] + eix=np.zeros((ne,3), dtype=int) + k=0 + for i in range(ne): + A = f.readline() + values = A.split(" ") + if len(values) == 6: + if int(values[2])==2: + bnd.append(int(values[5])) + nbnd=nbnd+1 + if len(values)>15: + eix[k,0]=int(values[12]) + eix[k,1]=int(values[14]) + eix[k,2]=int(values[16]) + k=k+1 + elif len(values)>7: + eix[k,0]=int(values[6]) + eix[k,1]=int(values[7]) + eix[k,2]=int(values[8]) + k=k+1 + ei=eix[range(k),:] + print("number of open boundary nodes read: "+str(nbnd)) + print("number of elements read: "+str(k)) + return xi, yi, zi, ei, bnd + +def WriteWW3Mesh(flo,x,y,z,e,bnd): + f=open(flo, 'w') + f.write("$MeshFormat\n") + f.write("2 0 8\n") + f.write("$EndMeshFormat\n") + f.write("$Nodes\n") + nn=len(x) + f.write(str(nn)+"\n") + for k in range(nn): + f.write(f"{k+1:8d} {x[k]:6f} {y[k]:6f} {z[k]:5f} \n") + ne=e.shape[0] + nbnd=len(bnd) + f.write("$EndNodes\n") + f.write("$Elements\n") + f.write(str(ne+nbnd)+"\n") + for k in range(nbnd): + f.write(str(k+1)+" 15 2 0 0 " + str(bnd[k])+"\n") + for k in range(ne): + f.write(str(nbnd+k+1)+" 2 3 0 "+str(k+1)+" 0 "+str(e[k,0])+" "+str(e[k,1])+" "+str(e[k,2])+"\n") + f.write("$EndElements\n") + f.close + +# compute length scale for each element e +def lengthscale(x, y, e): + ne=e.shape[0] + lengthscaleE=np.zeros(ne) + for k in range(ne): + if k % 10000 ==0: + print(k) + x1=x[e[k,0]-1] + y1=y[e[k,0]-1] + x2=x[e[k,1]-1] + y2=y[e[k,1]-1] + x3=x[e[k,2]-1] + y3=y[e[k,2]-1] + D3=Distance(x1,y1,x2,y2) + D1=Distance(x2,y2,x3,y3) + D2=Distance(x3,y3,x1,y1) + lengthscaleE[k]=(D1+D2+D3)/3 + # mean edge length + return lengthscaleE + +# compute area for each element e +def ElementArea(x, y, e): + ne=e.shape[0] + AreaE=np.zeros(ne) + for k in range(ne): + if k % 10000 ==0: + print(k) + x1=x[e[k,0]-1]; + y1=y[e[k,0]-1]; + x2=x[e[k,1]-1]; + y2=y[e[k,1]-1]; + x3=x[e[k,2]-1]; + y3=y[e[k,2]-1]; + D3=Distance(x1,y1,x2,y2) + D1=Distance(x2,y2,x3,y3) + D2=Distance(x3,y3,x1,y1) + S=(D1+D2+D3)/2 + A=np.sqrt(S * (S - D1) * (S - D2) * (S - D3)) + AreaE[k]=A + return AreaE + +# approximate mesh lengthscale at mesh nodes. +# lengthscale is the weighted average (by area) of surrounding elements +def ComputeNodeLengthScale(x,y,e): + nn=np.max(e[:]) + print("ComputeNodeLengthScale nn="+str(nn)) + areaE=ElementArea(x, y, e) + LengthScaleE=lengthscale(x, y, e) + + ne=e.shape[0] + areaT=np.zeros(nn) + lsN=np.zeros(nn) + for k in range(ne): + for j in range(3): + i=e[k,j]-1 + lsN[i]=lsN[i]+LengthScaleE[k]*areaE[k] + areaT[i]=areaT[i]+areaE[k] + for k in range(nn): + lsN[k]=lsN[k]/areaT[k] + return lsN + diff --git a/unst_msh_gen/regional/InterpolateBathymetry/GaussMarkov.py b/unst_msh_gen/regional/InterpolateBathymetry/GaussMarkov.py new file mode 100644 index 0000000..a854337 --- /dev/null +++ b/unst_msh_gen/regional/InterpolateBathymetry/GaussMarkov.py @@ -0,0 +1,134 @@ + +# +# Functions for Gauss Markov smoothing of bathymetry data. +# +# Common input variables: +# x,y,z are length nx vectors of observed depth (z) at point (x,y) +# xi,yi is the points for z to be intepolated to. +# +# InverseDistance : weighted inverse distance (squared) estimator +# +# GaussMarkovUnkMean : Gauss Markov smoothing with unkown mean (like ordinary kriging) +# +# GaussMarkov : Gauss Markov smoothing with kown mean (like simple kriging) +# Choices for mean are : +# "Zero" = 0 +# "Mean" = sample mean of z +# "Median" = sample median of z +# "Nearest" = value of z for closest point to (xi, yi) +# +# Two covariance functions are implemented: +# covmod="Exp" for exponential decay covariance as a function of distance or +# covmod="Sph" for spherical decay (finite suport) +# +# LengthScale is the (specified/ known) length scale in km at (xi, yi). +# LengthScale is intended to be proportional to mesh lengthscake at (xi, yi). +# + +import numpy as np + +#covmod="Sph" +covmod="Exp" +CovExp=2 # Exponent in covariance funtion, used if covmod="Exp", +IDExp=2 # exponent used in inverse distance estimator + +deg2km=111.132954 +deg2rad=np.pi/180 + +def InverseDistance(x,y,z,xi,yi): + d=DistanceV(x,y,xi,yi) + w=1.0/(d**IDExp) + zi=np.dot( z, w )/np.sum( w ) + return zi + +def GaussMarkov(x,y,z,xi,yi,LengthScale,Vobs,V,MeanTyp,CompErr): + nx=len(x) + f = np.zeros(nx) + C = np.zeros((nx,nx)) + for j in range(nx): + d = DistanceV(x,y,x[j],y[j]) + C[j,range(nx)]=CovarianceDistance(d,covmod,LengthScale,V) + C[j,j]=C[j,j] + Vobs + d = DistanceV(x,y,xi,yi) + f = CovarianceDistance(d,covmod,LengthScale,V) + detC = np.linalg.det(C) + if np.isclose(detC, 0): + zi=float("inf") + print("Cov matrix is nearly singular. Check for double input points, etc.") + if not CompErr: + return zi + else: + erri=float("inf") + return zi, erri + else: + match MeanTyp: + case "Zero": # simple kriging type + mu=0. + case "Mean": + mu=np.mean(z) # regional mean + case "Median": + mu=np.median(z) # regional median + case "Nearest": # nearest value + mu=z[np.argmax(f)] # max correlate(monotone)<==>closest point for process mean + case _: + mu=0. + w = np.linalg.solve(C, f) + zi=mu+np.dot(w,z-np.array(mu)) + if not CompErr: + return zi + else: + erri=np.sqrt( V - np.dot(f,w)) + return zi, erri + +def GaussMarkovUnkMean(x,y,z,xi,yi,LengthScale,Vobs,V,CompErr): + nx=len(x) + f = np.zeros(nx+1) + C = np.zeros((nx+1,nx+1)) + for j in range(nx): + d = DistanceV(x,y,x[j],y[j]) + C[j,range(nx)]=CovarianceDistance(d,covmod,LengthScale,V) + C[j,j]=C[j,j] + Vobs + C[nx,j]=1. + C[j,nx]=1. + d = DistanceV(x,y,xi,yi) + f[range(nx)] = CovarianceDistance(d,covmod,LengthScale,V) + C[nx,nx]=0 + f[nx]=1 + detC = np.linalg.det(C) + if np.isclose(detC, 0): + zi=float("inf") + w=zi+f + print("Cov matrix is likely singular.") + else: + w = np.linalg.solve(C, f) + zi= np.dot(w[range(nx)],z) + if not CompErr: + return zi + else: + erri=np.sqrt( V + np.dot( w[0:nx], np.matmul(C[0:nx,0:nx],w[0:nx])) -2.* np.dot(w[0:nx],f[0:nx]) ) + return zi, erri + +def DistanceV(x,y,x0,y0): #distance between list x,y and point x0,y0 + n=len(x) + d=np.zeros(n) + d=np.sqrt( + ( deg2km*np.cos( deg2rad*y0 )*(x-x0) )**2 + + ( deg2km*(y-y0))**2 ) + return d + +#from geopy import distance +def Distance(x0,y0,x1,y1): +# d = distance.great_circle(y0,x0,y1,x1).km + d=np.sqrt( + ( deg2km*np.cos(deg2rad*(y0+y1)/2 )*(x1-x0) )**2 + + ( deg2km*(y1-y0))**2 ) + return d + +def CovarianceDistance(d,model,ls,v): + if model == "Exp": + c = v*np.exp(- (d/ls )**CovExp ) + if model == "Sph": + c = v*( 1. - 1.5*(d/ls) + .5*((d/ls)**3) ) + c[np.where(d>ls)]=0. + return c + diff --git a/unst_msh_gen/regional/InterpolateBathymetry/InterpolateCRM.partscat.py b/unst_msh_gen/regional/InterpolateBathymetry/InterpolateCRM.partscat.py new file mode 100644 index 0000000..ff51dd1 --- /dev/null +++ b/unst_msh_gen/regional/InterpolateBathymetry/InterpolateCRM.partscat.py @@ -0,0 +1,371 @@ +###################################################################### +# This is the main interpolation engine. This script reads in a list +# of nodes created by DivideMeshNodes.py and interpolates bathymetry +# from a list of bathymetry data files to the nodes in the assigned +# list. The intent is to run this in NPart parallel jobs for a +# partition of mesh nodes into NPart sets. +# +# The user is responsible for specifing the bathymetry data sets +# in list "flnms" as well as the type of data each of these files +# in list "filetype". filetype[k] = 0 gridded local data, 1 gridded +# global data and 2 for scattered data. +# +# The user should define there observational error model and bacground +# process statistical model. +# +import os +import argparse +import time +import numpy as np +import netCDF4 as nc + +#import jigsawpy +import sys +from scipy.interpolate import RegularGridInterpolator +import math + +#from geopy import distance +import itertools +#from geopy.distance import haversine +#from pykrige.ok import OrdinaryKriging + +import FiniteElementMeshRoutines as FE +import GaussMarkov as GM + +# Name of mesh to interpolate bathymetry to +mshnm="HawaiiTest" + +mesh="meshes/"+mshnm+".msh" +OutDir=mshnm+".files/" + +# TextOutput = True # prints lots of information durring interpolation (may cause slowdown) +TextOutput = False + +# directory containg all of the bahymetry data files specified in flnms +BathyDir="/scratch3/NCEPDEV/climate/Keston.Smith/CoastalReliefModel/" + +######################################################## +# processed netcdf bathymetry files with variables: +# lon, lat and z. Specify corresponding file type, gridded or scattered, +# in list "filetype" below. +######################################################## +flnms=[ + "cmems_obs-sdb_glo_phy-comp_my-oa-100m-l4-s2_static.PointValues500m.nc", + "crm_vol1_2023.nc.S250m.VB.nc", + "crm_vol2_2023.nc.S250m.VB.nc", + "crm_vol3_2023.nc.S250m.VB.nc", + "crm_vol4_2023.nc.S250m.VB.nc", + "crm_vol5_2023.nc.S250m.VB.nc", + "crm_vol7_2024.nc.S250m.VB.nc", + "crm_vol9_2023.nc.S250m.VB.nc", + "crm_vol10_2023.nc.S250m.VB.nc", + "crm_vol6_2023.nc.S250m.VB.nc", + "crm_vol8_2023.nc.S250m.VB.nc", + "crm_southak.CRMformat.nc", + "hurl_bathy_60m_nwhi.CRMformat.nc.S250m.VB.nc", + "PIX/ngdc_bathy_10m_wake.CRM.nc", + "PIX/pibhmc_bathy_20m_jarvis.CRM.nc", + "PIX/pibhmc_bathy_20m_johnston.CRM.nc", + "PIX/pibhmc_bathy_20m_kingman.CRM.nc", + "PIX/pibhmc_bathy_40m_baker.CRM.nc", + "PIX/pibhmc_bathy_40m_howland.CRM.nc", + "PIX/pibhmc_bathy_40m_palmyra.CRM.nc", + "PIX/pibhmc_bathy_40m_rose.CRM.nc", + "PIX/pibhmc_bathy_40m_swains.CRM.nc", + "PIX/pibhmc_bathy_40m_vailuluu.CRM.nc", + "PIX/pibhmc_bathy_5m_palmyra.CRM.nc", + "PIX/sopac_bathy_50m_majuro_reef.CRM.nc", + "PIX/ngdc_bathy_180m_mariana.CRM.nc", + "ngdc_bathy_90m_amsamoa.crm.nc", + "RTopo_2_0_4_GEBCO_v2023_60sec_pixel.CRMformat.nc" + ] +######################################################## + +######################################################## +# Set filetype corresponding to files in flnms +# 0 : for gridded local data and +# 1 : for gridded global data and +# 2 : for scattered global data +######################################################## +nf=len(flnms) +filetype=np.zeros(nf, dtype=int) +filetype[0]=2 +filetype[nf-1]=1 +######################################################## + +Zmin=-11000. #deepest legit ocean depth value in case of mask fail +# nescesarry for data sets "crm_vol6_2023.nc.S250m.nc" and "crm_vol8_2023.nc.S250m.nc" +Zmax=0. # maximum value to include in interpolation - zero out land values or ignore land +Dmin=5./1000.# 5 meter minimum distance between observation points in km, prevent singularity +lambdaLL=.025 # set deg lat, lon search width for overlapping regions +#use Approximately NxTarget**2 points per data set, +#each linear system is approx 2*(NxTarget**2) +NxTarget=20 # N=20 ~ 10 grid points in each cardinal direction used in interpolating +NpointsMax=1000 +xlist=[] +ylist=[] +n=0 +nxl=np.zeros(len(flnms)) +nyl=np.zeros(len(flnms)) +xmax=np.zeros(len(flnms)) +xmin=np.zeros(len(flnms)) +ymax=np.zeros(len(flnms)) +ymin=np.zeros(len(flnms)) +for fl in flnms: +# print(f"'{fl}' has a length of {len(fl)}") +# if n>0: + data = nc.Dataset(BathyDir+fl,"r") + x=np.array(data["lon"][:]) + x=x%360 + y=np.array(data["lat"][:]) + xlist.append(list(x)) + ylist.append(list(y)) + nxl[n]=len(x) + nyl[n]=len(y) + #setup quick lookup table for file exlusion + xmax[n]=np.max(x)+lambdaLL*2 + xmin[n]=np.min(x)-lambdaLL*2 + ymax[n]=np.max(y)+lambdaLL*2 + ymin[n]=np.min(y)-lambdaLL*2 + n=n+1 + +def ZeroPadIntStr(N,K): + ZPNs=str(N).zfill(K) + return ZPNs + + +xi, yi, ei = FE.loadWW3MeshCoords(mesh) + +lsN=FE.ComputeNodeLengthScale(xi,yi,ei) + +print(np.min(lsN)) +print(np.mean(lsN)) +print(np.max(lsN)) + +N=int(sys.argv[1]) + +fln=OutDir+'NodeList.'+str(N)+'.txt' +floutID=OutDir+'InvDist.'+str(N)+'.txt' +floutGMM=OutDir+'GMM.'+str(N)+'.txt' +floutGMN=OutDir+'GMN.'+str(N)+'.txt' +floutGM0=OutDir+'GM0.'+str(N)+'.txt' +floutGMU=OutDir+'GMU.'+str(N)+'.txt' +floutClosest=OutDir+'ClosestValue.'+str(N)+'.txt' +floutNpts=OutDir+'NDataPoints.'+str(N)+'.txt' +floutLLS=OutDir+'LengthScale.'+str(N)+'.txt' +floutGMUerr= OutDir+'GMU.std.'+str(N)+'.txt' +floutGMNerr= OutDir+'GMN.std.'+str(N)+'.txt' +#read in nodes to interpolate to +f=open(fln,"r") +header = f.readline() +nn =int( f.readline()) + +#make local node set to interpolate to +xil=np.zeros(nn) +yil=np.zeros(nn) +LSl=np.zeros(nn) +for k in range(nn): + j = int(f.readline()) + xil[k]=xi[j] + yil[k]=yi[j] + LSl[k]=lsN[j] + +nn=len(xil) + +# set search width for each data set based on expected +# number of points to interpolate +SearchWidth=np.zeros(nf) +for j in range(nf): + x=np.array(xlist[j][:]) + dx=np.abs(x[1]-x[0]) + SearchWidth[j]=dx*float(NxTarget)/2. + print("search width(deg Lat lon) for file: "+flnms[j]+" = ",str(SearchWidth[j])) + +SearchWidth[0]=.05 +SearchWidth[nf-1]=.75*SearchWidth[nf-1] # smaller number for global bathy set + +# Initialize estimate fields and posterioir estimates +NumPoints=np.zeros(nn) +ziID=np.zeros(nn) +ziGMU=np.zeros(nn) +stdiGMU=np.zeros(nn) +ziGMN=np.zeros(nn) +stdiGMN=np.zeros(nn) +#ziGMM=np.zeros(nn) +#ziGM0=np.zeros(nn) +ziClosest=np.zeros(nn) +LocalLengthScale=np.zeros(nn) + +###################################################################### +# For each scattered data set, read in the scattered data here to +# one dimensional arrays x0, y0, and z0. This should be looped to +# accumulate all of the scatted datasets together. The only scattered +# data set used here is +# "cmems_obs-sdb_glo_phy-comp_my-oa-100m-l4-s2_static.PointValues500m.nc", +# so this is simply loaded here. +###################################################################### +fl=flnms[0] +data = nc.Dataset(BathyDir+fl,"r") +x0=data["lon"][:] +x0=x0%360 +y0=data["lat"][:] +z0=data["z"][:] +###################################################################### + +t0 = time.time() +for n in range(nn): + xp=xil[n]%360 + yp=yil[n] +# Set local length scale to use in Gauss Markov smoothing at node n +# LSp=2.*LSl[n] #probably better choice for spherical covarianve function + LSp=1.*LSl[n] #choice for exponential covarianve function + LocalLengthScale[n]=LSp + xs=[] + ys=[] + zs=[] + si=0 + for j in range(nf): + if filetype[j] <2 : #gridded data + if all([ xp < xmax[j],xp > xmin[j],yp < ymax[j],yp > ymin[j]]): + x=np.array(xlist[j][:]) + y=np.array(ylist[j][:]) + fl=flnms[j] + if TextOutput: + print(fl) + jx=np.array(np.where( np.abs(xp-x) < SearchWidth[j] )) + jx=list(itertools.chain.from_iterable(jx)) + jy=np.array(np.where( np.abs(yp-y) < SearchWidth[j] )) + jy=list(itertools.chain.from_iterable(jy)) + data = nc.Dataset(BathyDir+fl,"r") + for kx in jx: + for ky in jy: + if kx and ky: # not empty + z=data["z"][ky,kx] + if not z.mask: + IncludePoint=True + if len(xs)>0: #BEGIN -check for near duplicate points - causes singularity in GMS + d=GM.DistanceV(xs,ys,x[kx],y[ky]) + if np.min(d) Zmax:#exclude land values(should be optional) + # IncludePoint=False + if IncludePoint: # first point + zd=min(zd,Zmax) # trunkate interpolant to Zmax<=0 + xs.append(x[kx]) + ys.append(y[ky]) + zs.append(zd) + else: # global scattered data, filetype[j] == 2 + fl=flnms[j] + j=np.array( np.where( np.array(np.abs(xp-x0) < SearchWidth[j] ) & np.array(np.abs(yp-y0) < SearchWidth[j] ) ) ) + j=list(itertools.chain.from_iterable(j)) + j=np.array(j).flatten() + if j.size>0: + j=np.array(j).flatten() + x=x0[j] + y=y0[j] + zd=z0[j] + jp=np.where( zd <= 0.) + x=x[jp] + y=y[jp] + zd=zd[jp] + if TextOutput: + print("number of points from "+fl+" is " +str(len(zd)) ) + xs.extend(x) + ys.extend(y) + zs.extend(zd) + if TextOutput: + if n % 1 == 0: + print("interpolating to:"+str(yp)+":"+str(xp)) + t1 = time.time() + time_per_iter = (t1 - t0) / (n + 1) + time_remaining = (nn - n) * time_per_iter / 60 + print("Progress: "+str(n+1)+" of "+ str(nn)) + print(f"Time remaining: {time_remaining:.2f} minutes") + print(f"Average time per node: {time_per_iter:.2f} seconds") + print("node: "+str(n)+" uses "+str(len(xs))+" data points") + + Npoints=len(xs) + NumPoints[n]=Npoints + + #limit total number of points in interpolation (shouldn't come into effect much in practice) + if Npoints > NpointsMax: + if TextOutput: + print("node "+str(n)+" has "+str(Npoints)+" data points. taking nearest: "+str(NpointsMax)) + xsp=np.zeros(NpointsMax) + ysp=xsp + zsp=xsp + D=GM.Distance(xs,ys,xp,yp) + for k in range(NpointsMax): + ki=np.argmin(D) + xsp[k]=xs[ki] + ysp[k]=ys[ki] + zsp[k]=zs[ki] + D[ki]=float('inf') + xs=xsp + ys=ysp + zs=zsp + +##### Specify priori error statistics here############################# +# Here some simple error statistics are given: These can be made dataset +# dependent, depth dependent, etc. +# Assumptions regarding observation standard error: + ObsErr= max(1. , np.mean(np.abs(zs))/100. ) # 1 percent of local mean depth (m) + VarObs=ObsErr**2 #(m^2) +#Specify assumptions regarding background variance: + VarBG=10.*VarObs #(m^2) Background variance = 10 observation error variance +##### Done specify priori error statistics ############################ + + +##### Different depth estimators ###################################### + if TextOutput: + print("Number of obs = " +str(len(zs)) ) + print("std obs= " + str(np.sqrt(VarObs)) + " (m), std BG= " + str(np.sqrt(VarBG))+" (m)" ) + +# Inverse distance estimator + ziID[n]=GM.InverseDistance(xs,ys,zs,xp,yp) + +# Gauss-Markov smoothing with unkown mean (like ordinary kriging) + ziGMU[n], stdiGMU[n]=GM.GaussMarkovUnkMean(xs, ys, zs, xp, yp,LSp, VarObs,VarBG,True) + if TextOutput: + print("GMU: est= "+str(ziGMU[n])+", err= "+str(stdiGMU[n])) + +# Gauss-Markov smoothing with known mean (like simple kriging). Assumed mean is closest observation + ziGMN[n], stdiGMN[n]=GM.GaussMarkov(xs, ys, zs, xp, yp, LSp, VarObs,VarBG,"Nearest",True) + if TextOutput: + print("GMN: est= "+str(ziGMN[n])+", err= "+str(stdiGMN[n])) + +# Gauss-Markov smoothing with known mean (like simple kriging). Assumed mean is sample mean +# ziGMM[n]=GM.GaussMarkov(xs, ys, zs, xp, yp, LSp, VarObs,VarBG,"Mean",False) + +# Gauss-Markov smoothing with known mean (like simple kriging). Assumed mean is 0 +# ziGM0[n]=GM.GaussMarkov(xs, ys, zs, xp, yp, LSp, VarObs,VarBG,"Zero",False) + +# Nearest neighbor estimator + D=GM.DistanceV(xs,ys,xp,yp) + jc=np.argmin(D) + ziClosest[n]=zs[jc] + +###### Output estimates and other statistics to text files ########### +#Inverse distance output +np.savetxt(floutID , ziID , fmt='%.6f', delimiter='\n') +#Gauss Markov smoothing with known mean output +np.savetxt(floutGMN, ziGMN, fmt='%.6f', delimiter='\n') +#Std error (m) for Gauss Markov smoothing with known mean output +np.savetxt(floutGMNerr, stdiGMN, fmt='%.6f', delimiter='\n') +#Gauss Markov smoothing with unknown mean output +np.savetxt(floutGMU, ziGMU, fmt='%.6f', delimiter='\n') +#Std error (m) for Gauss Markov smoothing with unknown mean output +np.savetxt(floutGMUerr, stdiGMU, fmt='%.6f', delimiter='\n') +#Nearest neighbor estimate +np.savetxt(floutClosest, ziClosest, fmt='%.6f', delimiter='\n') +#Number of bathymetry observations used for each node +np.savetxt(floutNpts, NumPoints, fmt='%i', delimiter='\n') +#Mesh length scale approcimation at nodes +np.savetxt(floutLLS, LocalLengthScale, fmt='%.6f', delimiter='\n') + + diff --git a/unst_msh_gen/regional/InterpolateBathymetry/KnitOutputBackTogether.py b/unst_msh_gen/regional/InterpolateBathymetry/KnitOutputBackTogether.py new file mode 100644 index 0000000..99b9100 --- /dev/null +++ b/unst_msh_gen/regional/InterpolateBathymetry/KnitOutputBackTogether.py @@ -0,0 +1,41 @@ + +# +# This script takes the files generated in parallel from InterpolateCRM.partscat.py +# It should be called at the end of +# $ sbatch jobcardInterpBathy2Mesh +# when all independent interpolation problems are finished +# + +import numpy as np +import FiniteElementMeshRoutines as FE +import sys + +mshnm="HawaiiTest" +mesh="meshes/"+mshnm+".msh" +OutDir=mshnm+".files/" + +Nparts=int(sys.argv[1]) +Prefix=sys.argv[2] +xi, yi, ei = FE.loadWW3MeshCoords(mesh) +nn=len(xi) +NodeList=[] +si=np.zeros(nn) +for n in range(Nparts): + fli=OutDir+'NodeList.'+str(n)+'.txt' + fpi=open(fli, 'r') + fls=OutDir+Prefix+"."+str(n)+".txt" + fps=open(fls, 'r') + print("reading nodelist from: "+fli+", reading values from: "+fls) + header = fpi.readline() + nn0=int(fpi.readline()) + for k in range(nn0): + j = int(fpi.readline()) + value=float(fps.readline()) + si[j] = value +fps.close +fpi.close + +#write full field file with interpolated values +flout=mshnm+"."+Prefix+".txt" +np.savetxt(flout , si, fmt='%.6f',delimiter='\n') + diff --git a/unst_msh_gen/regional/InterpolateBathymetry/MakeCommonNetCDFFileFormat.py b/unst_msh_gen/regional/InterpolateBathymetry/MakeCommonNetCDFFileFormat.py new file mode 100644 index 0000000..fe0941a --- /dev/null +++ b/unst_msh_gen/regional/InterpolateBathymetry/MakeCommonNetCDFFileFormat.py @@ -0,0 +1,437 @@ + +# This is a script to put all gridded bathymetry datasets into the same format. +# The output files should follow the same format as the 2023 version of the +# Coastal Relief Model (CRM) available at +# https://www.ncei.noaa.gov/products/coastal-relief-model +# Makes files with same variable names, latitude/longitude conventions, and masking. +# +# To run the full script you will need the following files: +# +# crm_vol6.nc +# crm_vol8.nc +# crm_southak.nc +# pibhmc_bathy_60m_guam.nc +# ngdc_bathy_90m_amsamoa.nc +# hurl_bathy_60m_nwhi.nc +# RTopo_2_0_4_GEBCO_v2023_60sec_pixel.nc +# +# Sources for these files are within the script in comments. + +import numpy as np +import netCDF4 as nc +import math +import subprocess + +# Example 1: Basic command execution +# This will run 'ls -l' and print the output to the console. +subprocess.run(["ls", "-l"]) + +#[0->7] variables(dimensions): |S1 crs(), float64 lat(lat), float64 lon(lon), float32 z(lat, lon) +#[8->9]variables(dimensions): float64 x(x), float64 y(y), float32 z(y, x) +#[10]variables(dimensions): float64 lon(num_lon), float64 lat(num_lat), int16 bed_elevation(num_row, num_col) + +Zmin=-12000 # deepest valid value (m) - used to backup masking errors + +# from https://www.ncei.noaa.gov/products/coastal-relief-model +flTemplate="crm_vol1_2023.nc" +dataT = nc.Dataset(flTemplate,"r") +print(dataT) + + +""" +# from https://pae-paha.pacioos.hawaii.edu/erddap/info/srtm30plus_v11_bathy/index.html +flin="srtm30plus_v11_bathy.nc" +flout="srtm30plus_v11_bathy.CRMformat.nc" +data0 = nc.Dataset(flin,"r") +x=np.asarray(data0["lon"][:]) +y=np.asarray(data0["lat"][:]) +z=np.asarray(data0["elev"][:,:]) +#z=float(z) +j=np.where(np.isnan(z)) +z[j]=-99999 +j=np.where(z7] variables(dimensions): |S1 crs(), float64 lat(lat), float64 lon(lon), float32 z(lat, lon) +#[8->9]variables(dimensions): float64 x(x), float64 y(y), float32 z(y, x) +#[10]variables(dimensions): float64 lon(num_lon), float64 lat(num_lat), int16 bed_elevation(num_row, num_col) + +flnms=[ + "crm_vol1_2023.nc",#dx= 0.00027, 3600/deg + "crm_vol2_2023.nc", + "crm_vol3_2023.nc", + "crm_vol4_2023.nc", + "crm_vol5_2023.nc", + "crm_vol7_2024.nc", + "crm_vol9_2023.nc", + "crm_vol10_2023.nc", #problem with mask values + "crm_vol6_2023.nc",#dx= 0.0008 cut to 3 x 3 + "crm_vol8_2023.nc",#dx= 0.0008,1200 / deg + "hurl_bathy_60m_nwhi.CRMformat.nc", + "pibhmc_bathy_60m_guam.crm.nc", + "ngdc_bathy_90m_amsamoa.crm.nc" + ] + +count=0 +filval=-99999 +for fl in flnms: + count=count+1 + + Nsubsample=9 + Nstart=4 + if (fl=="crm_vol6_2023.nc"or fl=="crm_vol8_2023.nc" ): + Nsubsample=3 + Nstart=1 + if fl == "hurl_bathy_60m_nwhi.CRMformat.nc": + Nsubsample=5 + Nstart=2 + if (fl == "pibhmc_bathy_60m_guam.crm.nc" or "ngdc_bathy_90m_amsamoa.crm.nc"): + Nsubsample=3 + Nstart=1 + + data0 = nc.Dataset(fl,"r") + x=np.asarray(data0["lon"][:]) + y=np.asarray(data0["lat"][:]) + if fl == "crm_socal_1as_vers2.nc": + z=np.asarray(data0["Band1"][:]) + z=z.astype(np.float32) + else: + z=np.asarray(data0["z"][:]) + nx=len(x) + ny=len(y) + dx=x[1]-x[0] + dy=y[1]-y[0] + print(fl) + print(z.shape) + print("org. dx= "+str(dx)) + print(1.0/dx) + print("org. dy= "+str(dy)) + print(1.0/dy) + if ( (fl == "crm_vol10_2023.nc") or (fl == "crm_socal_1as_vers2.nc") ) : # This file has an un recognized fill value + print("fixing bad fill value in: "+fl) + jb=np.where(z==-9999) + z[jb]= -float("inf") + + W=np.ones((Nsubsample,Nsubsample))/(Nsubsample**2) + z = signal.convolve2d(z,W, mode='same') + x=x[Nstart :: Nsubsample] + y=y[Nstart :: Nsubsample] + z=z[Nstart :: Nsubsample,Nstart :: Nsubsample] + nx=len(x) + ny=len(y) +# flout=fl+'.S250m.nc' + flout=fl+'.S250m.VB.nc' + # This file has an un recognized fill value +# if fl == "crm_vol10_2023.nc": + if ( (fl == "crm_vol10_2023.nc") or (fl == "crm_socal_1as_vers2.nc") ) : # This file has an un recognized fill value + print("patching correct fill values in: "+flout) + jb=np.where(zlength(jWest)),%make translated copy + display(['duplicating coastal segment: ',int2str(k), ', nseg=',int2str(ns(k))]) + N1=N1+1; + S(N1).X=[lon+360,NaN]; + S(N1).Y=[lat,NaN]; + ns(N1)=length(lon); + end +end +N=length(S); +[tmp,j]=sort(-ns); +S=S(j);% sort to descending in length +ns=ns(j);% sort to descending in length + +lon=Blon;j=find(lon<90);lon(j)=lon(j)+360; +Blon=lon; + +%Make Bounding rectangle +Bx=[Blon(1),Blon(2),Blon(2),Blon(1),Blon(1)] +By=[Blat(1),Blat(1),Blat(2),Blat(2),Blat(1)] + +clear pslg +sxp=[];syp=[ ]; +xc=[];yc=[]; +N=length(S); +pslg.x=[]; +pslg.y=[]; +pslg.edges=[]; +nc=0; + + +minedeges=4 % minimum number of edges in land bounadry feature to include +minarea=1 % minimum area for islands to be included in boundary (not used in this version) + +%Loop below finds intersection of boanary rectangle with coastline segments to create outer boandary + + +earth=referenceSphere('Earth'); +DXM=10^10;%DXM=1 +mdx=inf+ones(N,1); +for k=1:N + isinbox(k)=0; + x=S(k).X(1:end-1);% remove trailing nan (-1) and endpoint==startpoint (-2) + y=S(k).Y(1:end-1); + mx=mean(x); + my=mean(y); + if length(x)>minedeges, + ji=find( insidepoly( x,y,Bx,By ) ); + if length(ji)>2 + [xi, yi,ii] = polyxpoly(x, y, Bx, By); + xc=[xc;xi(:)]; + yc=[yc;yi(:)]; + if ~isempty(xi) %modify ob + sxp=[sxp,x(1)];syp=[syp,y(1)]; + [iia,jja]=sort(ii(:,1));%sort to ascending order along segments + xia=xi(jja); + yia=yi(jja); + iis=ii(jja,1);%sorted into ascending order along segments + if ~insidepoly( x(1),y(1),Bx,By )% segment origonates outside box + nseg=length(xia); + for j=1:2:nseg-1, + xs=[xia(j),x( iis(j)+1:iis(j+1) ),xia(j+1)]; + ys=[yia(j),y( iis(j)+1:iis(j+1) ),yia(j+1)]; + dx=abs(xs(2:end)-xs(1:end-1) +i*[ys(2:end)-ys(1:end-1)] ); + mdx(k)=max(dx); + + if max(dx) island entirely inside bounding box + xs=[x(1:end-1)]; + ys=[y(1:end-1)]; + dx=abs(xs(2:end)-xs(1:end-1) +i*[ys(2:end)-ys(1:end-1)] ); + if and(length(xs)>2,max(dx)3, ar> + if mod(k,1000)==0,disp(['Land segments compleate: ',num2str(k/N)]);,end +end + +%OK - now revisit outer boundary! +eval(['save -v7.3 ',FileOutMatlab,' pslg Blon Blat isplot']); + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%restart script from here if needed +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%clear +%close all +eval(['load ',FileOutMatlab]); +%load pslgOSM.mat +%%Bx=[Blon(1),Blon(2),Blon(2),Blon(1),Blon(1)] +%%By=[Blat(1),Blat(1),Blat(2),Blat(2),Blat(1)] + +%make outer bound +n=length(pslg.x); +%pslg.x=[pslg.x,Bx(3:4)];%add northwest and north east box corner nodes +%pslg.y=[pslg.y,By(3:4)]; +pslg.x=[pslg.x,Bx(2:4)];%add northwest and north east box corner nodes +pslg.y=[pslg.y,By(2:4)]; + +nc=length(pslg.chains);%add northwest and north east box corner chains +pslg.chains(nc+1).nodes=n+1; +nc=length(pslg.chains); +pslg.chains(nc+1).nodes=n+2; +nc=length(pslg.chains); +pslg.chains(nc+1).nodes=n+3; +nc=length(pslg.chains); + +xx =153.0400 %south east corner of Austrilia +yy = Blat(1) %check that these are the endpoints described for j0, j1!! +[m0,j0]=min(abs(pslg.x+i*pslg.y-xx-i*yy)) +if isplot,plot(xx,yy,'ro',pslg.x(j0),pslg.y(j0),'bx');end + +xx = 288.3143%check that these are the endpoints described for j0, j1!! +yy = Blat(1) %south west corner of S. America +[m1,j1]=min(abs(pslg.x+i*pslg.y-xx-i*yy)) +if isplot,plot(xx,yy,'ro',pslg.x(j1),pslg.y(j1),'bx');end + +nc=length(pslg.chains); +pslg.chains(nc+1).nodes=[j0,j1]; +nc=length(pslg.chains); + +for k=1:nc + if mod(k,100)==0,disp(['Labeling chains compleate: ',num2str(k/nc)]);end + spx(k)=pslg.x(pslg.chains(k).nodes(1)); + spy(k)=pslg.y(pslg.chains(k).nodes(1)); + epx(k)=pslg.x(pslg.chains(k).nodes(end)); + epy(k)=pslg.y(pslg.chains(k).nodes(end)); + end + +%make boundary order index along boundary for start points +SN=10.*[max(abs(pslg.x))+max(abs(pslg.y))];%large number to seperate edges +c=0*spy; +Deps1=10^-10 +j=find(abs(spy-By(1))v); +[mm,m]=min(c(j)); +l=j(m); +epi=[epi,pslg.chains(l).nodes]; +obc=[obc,l]; +n=0; +%while(epi(end)~=epi(1)) +while isempty( find(epi(2:end)==epi(1)) ) + v=d(l); + j=find(c>v); + [mm,m]=min(c(j)); + l=j(m) + epi=[epi,pslg.chains(l).nodes]; + obc=[obc,l]; + if isplot==1, plot(pslg.x(epi),pslg.y(epi),'r.-');pause(.001);end +end +j=find(epi(2:end)==epi(1)); +epi=epi(1:(j+1)); +%vvvvvvvvvvv Add fixed grid points on open boundary +% to prevent "curved" boundaries due to projection details +xob=pslg.x(epi); +yob=pslg.y(epi); +dob=abs([xob(2:end)-xob(1:end-1)]+i*[yob(2:end)-yob(1:end-1)]); +dob=[dob,abs([xob(end)-xob(1)]+i*[yob(1)-yob(end)])]; +nb=length(xob); +dmin=1;%node spacing around boundary +epit=epi; +for k=1:nb-1 + d=abs([xob(k+1)-xob(k)]+i*[yob(k+1)-yob(k)]); + if d>dmin + npl=round(d/dmin)-1; + dx=xob(k+1)-xob(k); + dy=yob(k+1)-yob(k); + xp=xob(k)+dx*[1:npl-1]/npl; + yp=yob(k)+dy*[1:npl-1]/npl; + np=length(xp); + n=length(pslg.x); + pslg.x=[pslg.x,xp]; + pslg.y=[pslg.y,yp]; + NewEdges=[[epi(k),n+1];[[n+1:n+np-1]',[n+2:n+np]'];[n+np,epi(k+1)]]; + %pslg.edges=[pslg.edges;NewEdges]; + j=find(epi(k)==epit ); + epit=[epit(1:j),n+1:n+np,epit(j+1:end)]; + end +end +epi=epit; +% to prevent "curved" boundaries due to projection details +%^^^^^^^^^^^^ Add fixed grid points on open boundary + + +% add edges in obc +epiv=epi(:); +NOE=length(epiv) +OutterEdges=[epiv(1:NOE-1),epiv(2:NOE)]; +pslg.edges=[pslg.edges;OutterEdges]; + +eval(['save -v7.3 ',FileOutMatlab,' pslg Blon Blat isplot']); + +nodelist=[]; +for k=1:nc + if mod(k,100)==0,disp(['Finding interior chains compleate: ',num2str(k/nc)]);,end + if pslg.chains(k).nodes(1)==pslg.chains(k).nodes(end) + n=pslg.chains(k).nodes(1); + [inpoly,onpoly]=insidepoly( pslg.x(n), pslg.y(n),xob,yob); + if and(inpoly==1,onpoly==0) + nodelist=union(nodelist,pslg.chains(k).nodes); + end + end +end + +eval(['save -v7.3 ',FileOutMatlab,' pslg Blon Blat isplot nodelist epi']); + +nodelistXB=union(nodelist,epi); +pslgb=subpslgFast(pslg,nodelistXB); + +%remove "random" duplicate nodes +%z=pslgb.x+i*pslgb.y; +%[zu,j,k]=unique(z); +%pslgc=subpslgFast(pslgb,j); +pslgc=pslgb + +%remove duplicate edges +pslgc.edgesS=sort(pslgc.edges')'; +pslgc.edgesSU=unique(pslgc.edgesS,'rows') +pslgc.edges=pslgc.edgesSU; + +pslg=pslgc + +eval(['save -v7.3 ',FileOutMatlab,' pslg Blon Blat isplot']); + +%save PSLG to jigsaw .msh format +geom=pslg2geom(pslg) +savemsh(FileOutJigsaw,geom) + diff --git a/unst_msh_gen/regional/MeshGenTemplateDirectory/BuildBoundaryPSLGwOSM.m b/unst_msh_gen/regional/MeshGenTemplateDirectory/BuildBoundaryPSLGwOSM.m new file mode 100644 index 0000000..2699eea --- /dev/null +++ b/unst_msh_gen/regional/MeshGenTemplateDirectory/BuildBoundaryPSLGwOSM.m @@ -0,0 +1,351 @@ +% Script to build PSLG based on Open Street Map(OSM) ocastline database +% https://osmdata.openstreetmap.de/download/land-polygons-complete-4326.zip +% Before running this script run script MakeCoastalBoundariesOSM.m to smooth curves in GSHHS data to reflect +% desired coastal resolution in final mesh. + +CoastLineFile = 'GlobalCoastlineOSM.shp' +lonWest=129.91;lonEast=10.71;latSouth=-30.42;latNorth=79.99; + +S=shaperead(CoastLineFile) +FileOutJigsaw=[CoastLineFile(1:end-4),'.PSLG.msh'] +FileOutMatlab=[CoastLineFile(1:end-4),'.PSLGtmp.mat'] +isplot=0; + +%load GlobalCoastlineOSM.mat + + +%The overall objective of this project is to develop and implement into operations +% a Regional Wave Prediction System (RWPS) that fulfills the needs of NWS marine coastal, +% offshore, and high seas areas of responsibility. The domain of the RWPS will cover the Atlantic, +% Pacific, and Arctic oceans equal to the bounds of the Oceanic Domain as developed for the +% National Blend of Models (NBM). For reference the NBM Oceanic Domain has corner points of LL - 30.42S - 129.91E UR - 79.99N - 10.71E. + +Blon=[lonWest, lonEast]; +Blat=[latSouth,latNorth]; + + +N=length(S); +N1=N; +for k=1:N + if mod(k,1000)==0,k/N,end + lon=S(k).X(1:end-1);%remove trailing nan + lat=S(k).Y(1:end-1); + ns(k)=length(lon); + jWest=find(lon<90); + if(length(jWest)==ns(k)); + S(k).X=[lon+360,NaN]; + end + if and(0length(jWest)),%make translated copy + display(['duplicating coastal segment: ',int2str(k), ', nseg=',int2str(ns(k))]) + N1=N1+1; + S(N1).X=[lon+360,NaN]; + S(N1).Y=[lat,NaN]; + ns(N1)=length(lon); + end +end +N=length(S); +[tmp,j]=sort(-ns); +S=S(j);% sort to descending in length +ns=ns(j);% sort to descending in length + +lon=Blon;j=find(lon<90);lon(j)=lon(j)+360; +Blon=lon; + +%Make Bounding rectangle +Bx=[Blon(1),Blon(2),Blon(2),Blon(1),Blon(1)] +By=[Blat(1),Blat(1),Blat(2),Blat(2),Blat(1)] + +clear pslg +sxp=[];syp=[ ]; +xc=[];yc=[]; +N=length(S); +pslg.x=[]; +pslg.y=[]; +pslg.edges=[]; +nc=0; + + +minedeges=4 % minimum number of edges in land bounadry feature to include +minarea=1 % minimum area for islands to be included in boundary (not used in this version) + +%Loop below finds intersection of boanary rectangle with coastline segments to create outer boandary + +earth=referenceSphere('Earth'); +DXM=10^10;%DXM=1 +mdx=inf+ones(N,1); +for k=1:N + isinbox(k)=0; + x=S(k).X(1:end-1);% remove trailing nan (-1) and endpoint==startpoint (-2) + y=S(k).Y(1:end-1); + mx=mean(x); + my=mean(y); + if length(x)>minedeges, + ji=find( insidepoly( x,y,Bx,By ) ); + if length(ji)>2 + [xi, yi,ii] = polyxpoly(x, y, Bx, By); + xc=[xc;xi(:)]; + yc=[yc;yi(:)]; + if ~isempty(xi) %modify ob + sxp=[sxp,x(1)];syp=[syp,y(1)]; + [iia,jja]=sort(ii(:,1));%sort to ascending order along segments + xia=xi(jja); + yia=yi(jja); + iis=ii(jja,1);%sorted into ascending order along segments + if ~insidepoly( x(1),y(1),Bx,By )% segment origonates outside box + nseg=length(xia); + for j=1:2:nseg-1, + xs=[xia(j),x( iis(j)+1:iis(j+1) ),xia(j+1)]; + ys=[yia(j),y( iis(j)+1:iis(j+1) ),yia(j+1)]; + dx=abs(xs(2:end)-xs(1:end-1) +i*[ys(2:end)-ys(1:end-1)] ); + mdx(k)=max(dx); + + if max(dx) island entirely inside bounding box + xs=[x(1:end-1)]; + ys=[y(1:end-1)]; + dx=abs(xs(2:end)-xs(1:end-1) +i*[ys(2:end)-ys(1:end-1)] ); + if and(length(xs)>2,max(dx)3, ar> + if mod(k,1000)==0,disp(['Land segments compleate: ',num2str(k/N)]);,end +end + +%OK - now revisit outer boundary! +eval(['save -v7.3 ',FileOutMatlab,' pslg Blon Blat isplot']); + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%restart script from here if needed +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%clear +%close all +eval(['load ',FileOutMatlab]); +%load pslgOSM.mat +%%Bx=[Blon(1),Blon(2),Blon(2),Blon(1),Blon(1)] +%%By=[Blat(1),Blat(1),Blat(2),Blat(2),Blat(1)] + +%make outer bound +n=length(pslg.x); +%pslg.x=[pslg.x,Bx(3:4)];%add northwest and north east box corner nodes +%pslg.y=[pslg.y,By(3:4)]; +pslg.x=[pslg.x,Bx(2:4)];%add northwest and north east box corner nodes +pslg.y=[pslg.y,By(2:4)]; + +nc=length(pslg.chains);%add northwest and north east box corner chains +pslg.chains(nc+1).nodes=n+1; +nc=length(pslg.chains); +pslg.chains(nc+1).nodes=n+2; +nc=length(pslg.chains); +pslg.chains(nc+1).nodes=n+3; +nc=length(pslg.chains); + +xx =153.0400 %south east corner of Austrilia +yy = Blat(1) %check that these are the endpoints described for j0, j1!! +[m0,j0]=min(abs(pslg.x+i*pslg.y-xx-i*yy)) +if isplot,plot(xx,yy,'ro',pslg.x(j0),pslg.y(j0),'bx');end + +xx = 288.3143%check that these are the endpoints described for j0, j1!! +yy = Blat(1) %south west corner of S. America +[m1,j1]=min(abs(pslg.x+i*pslg.y-xx-i*yy)) +if isplot,plot(xx,yy,'ro',pslg.x(j1),pslg.y(j1),'bx');end + +nc=length(pslg.chains); +pslg.chains(nc+1).nodes=[j0,j1]; +nc=length(pslg.chains); + +for k=1:nc + if mod(k,100)==0,disp(['Labeling chains compleate: ',num2str(k/nc)]);end + spx(k)=pslg.x(pslg.chains(k).nodes(1)); + spy(k)=pslg.y(pslg.chains(k).nodes(1)); + epx(k)=pslg.x(pslg.chains(k).nodes(end)); + epy(k)=pslg.y(pslg.chains(k).nodes(end)); + end + +%make boundary order index along boundary for start points +SN=10.*[max(abs(pslg.x))+max(abs(pslg.y))];%large number to seperate edges +c=0*spy; +Deps1=10^-10 +j=find(abs(spy-By(1))v); +[mm,m]=min(c(j)); +l=j(m); +epi=[epi,pslg.chains(l).nodes]; +obc=[obc,l]; +n=0; +%while(epi(end)~=epi(1)) +while isempty( find(epi(2:end)==epi(1)) ) + v=d(l); + j=find(c>v); + [mm,m]=min(c(j)); + l=j(m) + epi=[epi,pslg.chains(l).nodes]; + obc=[obc,l]; + if isplot==1, plot(pslg.x(epi),pslg.y(epi),'r.-');pause(.001);end +end +j=find(epi(2:end)==epi(1)); +epi=epi(1:(j+1)); +%vvvvvvvvvvv Add fixed grid points on open boundary +% to prevent "curved" boundaries due to projection details +xob=pslg.x(epi); +yob=pslg.y(epi); +dob=abs([xob(2:end)-xob(1:end-1)]+i*[yob(2:end)-yob(1:end-1)]); +dob=[dob,abs([xob(end)-xob(1)]+i*[yob(1)-yob(end)])]; +nb=length(xob); +dmin=1;%node spacing around boundary +epit=epi; +for k=1:nb-1 + d=abs([xob(k+1)-xob(k)]+i*[yob(k+1)-yob(k)]); + if d>dmin + npl=round(d/dmin)-1; + dx=xob(k+1)-xob(k); + dy=yob(k+1)-yob(k); + xp=xob(k)+dx*[1:npl-1]/npl; + yp=yob(k)+dy*[1:npl-1]/npl; + np=length(xp); + n=length(pslg.x); + pslg.x=[pslg.x,xp]; + pslg.y=[pslg.y,yp]; + NewEdges=[[epi(k),n+1];[[n+1:n+np-1]',[n+2:n+np]'];[n+np,epi(k+1)]]; + %pslg.edges=[pslg.edges;NewEdges]; + j=find(epi(k)==epit ); + epit=[epit(1:j),n+1:n+np,epit(j+1:end)]; + end +end +epi=epit; +% to prevent "curved" boundaries due to projection details +%^^^^^^^^^^^^ Add fixed grid points on open boundary + + +% add edges in obc +epiv=epi(:); +NOE=length(epiv) +OutterEdges=[epiv(1:NOE-1),epiv(2:NOE)]; +pslg.edges=[pslg.edges;OutterEdges]; + +eval(['save -v7.3 ',FileOutMatlab,' pslg Blon Blat isplot']); + +nodelist=[]; +for k=1:nc + if mod(k,100)==0,disp(['Finding interior chains compleate: ',num2str(k/nc)]);,end + if pslg.chains(k).nodes(1)==pslg.chains(k).nodes(end) + n=pslg.chains(k).nodes(1); + [inpoly,onpoly]=insidepoly( pslg.x(n), pslg.y(n),xob,yob); + if and(inpoly==1,onpoly==0) + nodelist=union(nodelist,pslg.chains(k).nodes); + end + end +end + +eval(['save -v7.3 ',FileOutMatlab,' pslg Blon Blat isplot nodelist epi']); + +nodelistXB=union(nodelist,epi); +pslgb=subpslgFast(pslg,nodelistXB); + +%remove "random" duplicate nodes +%z=pslgb.x+i*pslgb.y; +%[zu,j,k]=unique(z); +%pslgc=subpslgFast(pslgb,j); +pslgc=pslgb + +%remove duplicate edges +pslgc.edgesS=sort(pslgc.edges')'; +pslgc.edgesSU=unique(pslgc.edgesS,'rows') +pslgc.edges=pslgc.edgesSU; + +pslg=pslgc + +eval(['save -v7.3 ',FileOutMatlab,' pslg Blon Blat isplot']); + +%save PSLG to jigsaw .msh format +geom=pslg2geom(pslg) +savemsh(FileOutJigsaw,geom) + diff --git a/unst_msh_gen/regional/MeshGenTemplateDirectory/DownloadShapeFiles.sh b/unst_msh_gen/regional/MeshGenTemplateDirectory/DownloadShapeFiles.sh new file mode 100755 index 0000000..668d9eb --- /dev/null +++ b/unst_msh_gen/regional/MeshGenTemplateDirectory/DownloadShapeFiles.sh @@ -0,0 +1,51 @@ +#!/bin/bash + +# Script to downoad files used in mesh generation for RWPS +echo "Downloading coastline shapefiles for boundary definition" + +############################################################################################################# +######################## Download Coastline Shape Files #################################################### +############################################################################################################# + +#US coastline data +echo "Downloading US coastline" +wget --output-document tl_2023_us_coastline.zip https://www2.census.gov/geo/tiger/TIGER2023/COASTLINE/tl_2023_us_coastline.zip + +echo "Downloading Global Self-consistent, Hierarchical, High-resolution Shorelines (GSHHS)" +#GSHHG global coastline data +#wget --output-document gshhg-shp-2.3.7.zip http://www.soest.hawaii.edu/pwessel/gshhg/gshhg-shp-2.3.7.zip +wget --output-document gshhg-shp-2.3.7.zip https://www.ngdc.noaa.gov/mgg/shorelines/data/gshhg/latest/gshhg-shp-2.3.7.zip + +echo "Downloading OpenStreetMap shoreline (OSM)" +#OSM global coastline data +wget --output-document land-polygons-complete-4326.zip https://osmdata.openstreetmap.de/download/land-polygons-complete-4326.zip + +############################################################################################################# +######################## 60 second Global Bathymetry######################################################### + +echo "Downloading global bathymetry" +wget https://github.com/dengwirda/dem/releases/download/v0.1.1/RTopo_2_0_4_GEBCO_v2024_60sec_pixel.zip + +############################################################################################################# + +mkdir ../Data +mkdir ../Data/us_coastline/ +unzip tl_2023_us_coastline.zip +mv tl_2023_us_coastline.* ../Data/us_coastline/ +echo "US coastline data moved to ../Data/us_coastline/" + +unzip gshhg-shp-2.3.7.zip +mv GSHHS_shp/ ../Data/ +mv WDBII_shp ../Data/ +mv gshhg-shp-2.3.7.zip ../Data +echo "GSHHS global coastline data moved to ../Data/GSHHS_shp/" + +unzip land-polygons-complete-4326.zip +mkdir ../Data/openstreetmap_land +mv land-polygons-complete-* ../Data/openstreetmap_land/ +echo "OSM global coastline data moved to ../Data/openstreetmap_land/" + +unzip RTopo_2_0_4_GEBCO_v2024_60sec_pixel.zip +mkdir ../Data/Bathymetry +mv RTopo_2_0_4_GEBCO_v* ../Data/Bathymetry/ +echo "Global bathymetry data moved to ../Data/Bathymetry/" diff --git a/unst_msh_gen/regional/MeshGenTemplateDirectory/ExtraPointsOfIntrest.m b/unst_msh_gen/regional/MeshGenTemplateDirectory/ExtraPointsOfIntrest.m new file mode 100644 index 0000000..e4c3c18 --- /dev/null +++ b/unst_msh_gen/regional/MeshGenTemplateDirectory/ExtraPointsOfIntrest.m @@ -0,0 +1,117 @@ +function [xus,yus]=ExtraPointsOfIntrest +xus=[]; +yus=[]; + +%Now add Pacific Territories and COFA points +%------------------------------------------------------------------------ +%NWS Pacific Region, via the Compact of Free Association, +% %oversees operations of 5 Weather Service Offices (WSO) across +% Micronesia in the Republic of Palau, Federated States of Micronesia +% and the Republic of the Marshall Islands. WFO Guam provides routine +% forecasts as well as WWA services for these areas. -Eric Lau +%Palau,Yap, Chuuk, Pohnpei, Majuro, Pago Pago, Wake island + +%Wake Island +coord=[19.2796,166.6499];%E +xus=[xus,coord(2)];yus=[yus,coord(1)]; +%Palau +coord=[7.4942, 134.5690] +xus=[xus,coord(2)];yus=[yus,coord(1)]; +%Yap:9.5557° N, 138.1399° E +coord=[9.5557, 138.1399] +xus=[xus,coord(2)];yus=[yus,coord(1)]; +%Chuuk: 7°25′N 151°47′E +coord=[7.374227, 151.754606]%E +xus=[xus,coord(2)];yus=[yus,coord(1)]; +%Pohnpei › Coordinates: 6.8519° N, 158.2147° E +coord=[6.8519, 158.2147] +xus=[xus,coord(2)];yus=[yus,coord(1)]; +%Majuro › Coordinates7.0667° N, 171.2667° E +coord=[7.0667, 171.2667] +xus=[xus,coord(2)];yus=[yus,coord(1)]; +%Pago Pago › Coordinates: 14.2732° S, 170.7030° W +coord=[-14.2732, -170.7030]% SW +xus=[xus,coord(2)];yus=[yus,coord(1)]; +% Kosrae › Coordinates :5.3096° N, 162.9815° E +coord=[5.3096, 162.9815] +xus=[xus,coord(2)];yus=[yus,coord(1)]; +%------------------------------------------------------------------------ + +%Marshall Islands +%Majuro +coord=[7.0667, 171.2667] +%xus=[xus,coord(2)];yus=[yus,coord(1)]; +%Ebeye +coord=[8.7815, 167.7373] +%xus=[xus,coord(2)];yus=[yus,coord(1)]; +%Micronesia +%Kolonia, +coord=[6.9636, 158.2102] +%xus=[xus,coord(2)];yus=[yus,coord(1)]; +%Pohnpei, +coord=[6.8519, 158.2147] +%xus=[xus,coord(2)];yus=[yus,coord(1)]; +% Chuuk-Weno +coord=[7.4523, 151.8422] +%xus=[xus,coord(2)];yus=[yus,coord(1)]; +% Tofol +coord=[5.3256, 163.0086] +%xus=[xus,coord(2)];yus=[yus,coord(1)]; +%Colonia -between Palau and Guam +coord=[9.5164,138.1222] +%xus=[xus,coord(2)];yus=[yus,coord(1)]; +n1=length(xus); +%------------------------------------------------------------------------ +%Points from Curt +%Howland Island -Baker +coord=[0.8113, -176.6183]%W +xus=[xus,coord(2)];yus=[yus,coord(1)]; +%Johnston atoll +coord=[16.7295, -169.5336]%W +xus=[xus,coord(2)];yus=[yus,coord(1)]; +%Palmyra +coord=[5.8885, -162.0787]%W +xus=[xus,coord(2)];yus=[yus,coord(1)]; +%Jarvis Island +coord=[0.3744, -159.9967]%W +xus=[xus,coord(2)];yus=[yus,coord(1)]; +%Baker Island +%0.1936° N, 176.4769° W +coord=[0.1936,-176.4769 ]%W +xus=[xus,coord(2)];yus=[yus,coord(1)]; +%------------------------------------------------------------------------ + coord=[18.4101, -75.0115 ]%W + xus=[xus,coord(2)];yus=[yus,coord(1)]; + %If your team is going to the effort to add resolution for Majuro in RMI, I would strongly suggest doing the same for these atolls in RMI: + %1. Kwajalein Atoll, which is home to part of the Ronald Regard Ballistic Missile Test Site (https://home.army.mil/kwajalein/index.php) and the underprediction of wave heights by NWS' WaveWatchIII model in January 2024, which I discussed on our last call, is what led to significant damage at the base, per: https://www.youtube.com/shorts/jH-pGoQDdcg + %2. Enewetak Atoll, which is the home of the Runit Dome (https://en.wikipedia.org/wiki/Runit_Island), is threatened by wave-driven overwash that has serious implications for the US Department of State, Department of Defense/War, and Department of the Interior via the Intergovernmental Compact of Free Association (https://www.doi.gov/oia/compacts-of-free-association). + %3. Bikini Atoll, for similar reasons as Enewetak, although the radionuclides are all over the place and not all dumped in one location. + %I would note that most of the atolls drop off at a 70-80 degree slope from approximately 30 m depth (which is generally less than 1000 m from shore) to over 1000 m depth, so there is not a need for a large region of increasing resolution to capture a broad continental shelf as characterizes CONUS. + + %Kwajalein Atoll - 9.1898° N, 167.4243° E + %Enewetak Atoll - 11.4654° N, 162.1890° E + %Bikini Atoll - 11.6065° N, 165.3768° E + + %Kwajalein Atoll - 9.1898° N, 167.4243° E + coord=[9.1898, 167.4243 ]%W + xus=[xus,coord(2)];yus=[yus,coord(1)]; + %Enewetak Atoll - 11.4654° N, 162.1890° E + coord=[ 11.4654, 162.1890]%W + xus=[xus,coord(2)];yus=[yus,coord(1)]; + %Bikini Atoll - 11.6065° N, 165.3768° E + coord=[11.6065, 165.3768 ]%W + xus=[xus,coord(2)];yus=[yus,coord(1)]; + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%add points to off shore banks we want to refine here we have Georges Bank +%and banks around the Bahamas. + +xFB= [ -79.9909 -78.1963 -78.3957] +yFB= [23.7978 26.8928 24.1530] +xGB = -67.4517 +yGB = 41.3567 + +xus=[xus(:);xFB(:);xGB(:)]'; +yus=[yus(:);yFB(:);yGB(:)]'; + +xus=LonCon(xus); diff --git a/unst_msh_gen/regional/MeshGenTemplateDirectory/FilterRoutinesNM.py b/unst_msh_gen/regional/MeshGenTemplateDirectory/FilterRoutinesNM.py new file mode 100644 index 0000000..5a230ae --- /dev/null +++ b/unst_msh_gen/regional/MeshGenTemplateDirectory/FilterRoutinesNM.py @@ -0,0 +1,426 @@ + +# The DEM file used below can be found at: +# https://github.com/dengwirda/dem/releases/tag/v0.1.1 +import os +import argparse +import configparser +import numpy as np +import netCDF4 as nc + +import jigsawpy + +from scipy.interpolate import RegularGridInterpolator +from scipy.sparse import csr_matrix +from scipy.sparse.csgraph import connected_components + +#from spacing import * + +def parse_input_args(): + parser = argparse.ArgumentParser(description='Create a mask file with multiple methods.') + parser.add_argument('--config', type=str, required=True, help='Path to the configuration file.') + args = parser.parse_args() + return args + +def load_configuration(config_path): + config = configparser.ConfigParser() + config.read(config_path) + + # Creating a dictionary and populating it with configuration settings + configurations = { + 'mesh_file': config.get('MeshSettings', 'mesh_file', fallback=''), + 'ww3_mesh_file':config.get('MeshSettings', 'WW3_mesh_file', fallback=''), + 'hfun_hmax': float(config.get('MeshSettings', 'hfun_hmax', fallback='100')), + 'black_sea': config.getint('CommandLineArgs', 'black_sea', fallback=3), + 'mask_file': config.get('CommandLineArgs', 'mask_file', fallback=''), + 'hmax': float(config.get('Spacing', 'hmax', fallback='100.0')), + 'hshr': float(config.get('Spacing', 'hshr', fallback='100')), + 'nwav': int(config.get('Spacing', 'nwav', fallback='400')), + 'hmin': float(config.get('Spacing', 'hmin', fallback='100.0')), + 'dhdx': float(config.get('Spacing', 'dhdx', fallback='0.05')), + 'dem_file': config.get('DataFiles', 'dem_file', fallback='') + } + return configurations + +ISOLATED = 30000. # min surface area [km^2] + +# just global objects, to keep things simple... +geom = jigsawpy.jigsaw_msh_t() +spac = jigsawpy.jigsaw_msh_t() +mesh = jigsawpy.jigsaw_msh_t() +opts = jigsawpy.jigsaw_jig_t() + + + +def inject_dem(): + +# args = parse_input_args() +# configurations = load_configuration(args.config) + +#-- remap a DEM on to the vertices of the mesh + + print("*inject-dem...") + + # Load the DEM file from the config + #dem_file = configurations['dem_file'] + #data = nc.Dataset(dem_file,"r") + +# data = nc.Dataset("../RWPS/Data/RTopo_2_0_4_GEBCO_v2023_60sec_pixel.nc","r") +# data = nc.Dataset("/scratch3/NCEPDEV/climate/Keston.Smith/RWPS/Data/RTopo_2_0_4_GEBCO_v2023_60sec_pixel.nc","r") + data = nc.Dataset("../Data/Bathymetry/RTopo_2_0_4_GEBCO_v2024_60sec_pixel.nc","r") + xlon = np.asarray(data["lon"][:]) + ylat = np.asarray(data["lat"][:]) + elev = np.asarray(data["bed_elevation"][:]) + \ + np.asarray(data["ice_thickness"][:]) + + xmid = 0.5 * (xlon[:-1:] + xlon[1::]) + ymid = 0.5 * (ylat[:-1:] + ylat[1::]) + + ffun = RegularGridInterpolator( + (ymid, xmid), elev, + bounds_error=False, fill_value=None) + + vert = mesh.point["coord"] + + mids =(vert[mesh.tria3["index"][:, 0], :] + + vert[mesh.tria3["index"][:, 1], :] + + vert[mesh.tria3["index"][:, 2], :] + ) / 3.0 + + vsph = jigsawpy.R3toS2(geom.radii, vert) + vsph*= 180. / np.pi + + mesh.value = ffun((vsph[:, 1], vsph[:, 0])) + + msph = jigsawpy.R3toS2(geom.radii, mids) + msph*= 180. / np.pi + + mesh.vmids = ffun((msph[:, 1], msph[:, 0])) + + # save lon-lat at cell centres + mesh.smids = np.zeros( + (mesh.tria3.size, 2), dtype=np.float64) + mesh.smids[:, 0] = msph[:, 0] + mesh.smids[:, 1] = msph[:, 1] + + +def tri_to_tri(tria): + +#-- return tria-to-tria adj. as a sparse graph + + # non-unique edges in tris + edge = np.empty((0, 2), dtype=np.int32) + tris = np.empty((0), dtype=np.int32) + edge = np.concatenate((edge, + tria[:, (0, 1)]), axis=0) + tris = np.concatenate((tris, + np.arange(0, tria.shape[0]))) + + edge = np.concatenate((edge, + tria[:, (1, 2)]), axis=0) + tris = np.concatenate((tris, + np.arange(0, tria.shape[0]))) + + edge = np.concatenate((edge, + tria[:, (2, 0)]), axis=0) + tris = np.concatenate((tris, + np.arange(0, tria.shape[0]))) + + # which edges match to which? + edge = np.sort(edge, axis=1) + imap = np.argsort(edge[:, 1], kind="stable") + edge = edge[imap, :] + tris = tris[imap] + imap = np.argsort(edge[:, 0], kind="stable") + edge = edge[imap, :] + tris = tris[imap] + + diff = edge[1::, :] - edge[:-1:, :] + + same = np.argwhere(np.logical_and.reduce(( + diff[:, 0] == 0, + diff[:, 1] == 0))).ravel() + + # tris[same] and tris[same+1] share + rows = np.concatenate(( + tris[same], tris[same+1])) + cols = np.concatenate(( + tris[same+1], tris[same])) + data = np.ones(rows.size, dtype=np.int8) + + # ith tri is adj. to tri in ith row + return csr_matrix((data, (rows, cols))) + + +def filter_dry(mesh, mask): + +#-- require dry cells > 1 dry edge, and large + + print("*filter-dry...") + + filt = np.logical_not(mask) + tris = np.argwhere(filt).ravel() + + conn = tri_to_tri(mesh.tria3["index"][filt, :]) + + # require dry to be adj. >=1 dry cell + isol = np.sum(conn, axis=1) <= 1 + isol = np.ravel(isol) +#KWS COMMENT BELOW + + # delete groups of dry if too small + nprt, part = connected_components( + conn, directed=False, return_labels=True) + + tris = np.argwhere(filt).ravel() + for iprt in range(nprt): + itri = np.argwhere(part == iprt) + if (itri.size <= 2): mask[tris[itri]] = True + +#KWS COMMENT ABOVE + + # otherwise mark isolated cell as ocn + mask[tris[isol]] = True + + return mask + + +def filter_wet(mesh, mask): + +#-- require wet cells > 1 wet edge, and large + + print("*filter-wet...") + + tris = np.argwhere(mask).ravel() + + conn = tri_to_tri(mesh.tria3["index"][mask, :]) + + # require wet to be adj. >=1 wet cell + isol = np.sum(conn, axis=1) <= 1 + isol = np.ravel(isol) + + # delete groups of wet if too small + nprt, part = connected_components( + conn, directed=False, return_labels=True) + + area = jigsawpy.trivol2( + mesh.point["coord"], + mesh.tria3["index"][mask, :]) + + for iprt in range(nprt): + itri = np.argwhere(part == iprt) + asum = np.sum(area[itri]) + #print(asum) + if asum < ISOLATED: mask[tris[itri]] = False + + # otherwise mark isolated cell as dry + mask[tris[isol]] = False + + return mask + + +def filter_ocn(): + +# args = parse_input_args() +# configurations = load_configuration(args.config) + +#-- use the remapped elev. to keep ocean cells + + print("*filter-ocn...") +#KWS not sure what this is +# elev =(mesh.value[mesh.tria3["index"][:, 0]] +# + mesh.value[mesh.tria3["index"][:, 1]] +# + mesh.value[mesh.tria3["index"][:, 2]] +# + mesh.vmids) / 4.0 + elev =(mesh.value[mesh.tria3["index"][:, 0]] + + mesh.value[mesh.tria3["index"][:, 1]] + + mesh.value[mesh.tria3["index"][:, 2]] + ) / 3.0 + # Define the Caspian Sea region + caspian_lat_min = 34.5 + caspian_lat_max = 50.0 + caspian_lon_min = 44.5 + caspian_lon_max = 55.5 + + # Define the black Sea region + blacksea_lat_min = 40 + blacksea_lat_max = 47.25 + blacksea_lon_min = 26.15 + blacksea_lon_max = 41.5 + + # Define the additional region1 + additional_lat_min = 39.95 + additional_lat_max = 40.6 + additional_lon_min = 26 + additional_lon_max = 26.8 + + # Define the additional region2 + additional_lat_min2 = 40.3 + additional_lat_max2 = 40.6 + additional_lon_min2 = 26.8 + additional_lon_max2 = 30 + + # Define the additional region3 + additional_lat_min3 = 40.6 + additional_lat_max3 = 41.25 + additional_lon_min3 = 28.9 + additional_lon_max3 = 29.1 + + """ +# Print the elevations in the additional region + additional_elev = elev[np.logical_and.reduce(( + mesh.smids[:, 1] >= additional_lat_min, + mesh.smids[:, 1] <= additional_lat_max, + mesh.smids[:, 0] >= additional_lon_min, + mesh.smids[:, 0] <= additional_lon_max + ))] + print("Elevations in the additional region:") + print(additional_elev) + +# Print the elevations in the additional region2 + additional_elev2 = elev[np.logical_and.reduce(( + mesh.smids[:, 1] >= additional_lat_min2, + mesh.smids[:, 1] <= additional_lat_max2, + mesh.smids[:, 0] >= additional_lon_min2, + mesh.smids[:, 0] <= additional_lon_max2 + ))] + print("Elevations in the additional region2:") + print(additional_elev2) + +# Print the elevations in the additional region3 + additional_elev3 = elev[np.logical_and.reduce(( + mesh.smids[:, 1] >= additional_lat_min3, + mesh.smids[:, 1] <= additional_lat_max3, + mesh.smids[:, 0] >= additional_lon_min3, + mesh.smids[:, 0] <= additional_lon_max3 + ))] + print("Elevations in the additional region3:") + print(additional_elev3) + + """ + + # zssh, to cull elev. against + surf = np.zeros(elev.shape, dtype=np.float32) + # Update the surf array to include both regions + # Define the Caspian Sea region + caspian_region = np.logical_and.reduce(( + mesh.smids[:, 1] >= caspian_lat_min, + mesh.smids[:, 1] <= caspian_lat_max, + mesh.smids[:, 0] >= caspian_lon_min, + mesh.smids[:, 0] <= caspian_lon_max + )) + + blacksea_region = np.logical_and.reduce(( + mesh.smids[:, 1] >= blacksea_lat_min, + mesh.smids[:, 1] <= blacksea_lat_max, + mesh.smids[:, 0] >= blacksea_lon_min, + mesh.smids[:, 0] <= blacksea_lon_max + )) + + keep = elev <= surf # only keep tri with wet elev + + + + # iterate on dry cells until none "isolated" + #KWS remove filter dry for Non Global mesh +# if (False): +# knum = np.count_nonzero(keep) +# while (True): +# keep = filter_dry(mesh, keep) +# if (np.count_nonzero(keep) == knum): break +# knum = np.count_nonzero(keep) +# mesh.tria3 = mesh.tria3[keep] + + # iterate on wet cells until none "isolated" + keep = np.ones(mesh.tria3.size, dtype=bool) + knum = np.count_nonzero(keep) + while (True): + keep = filter_wet(mesh, keep) + if (np.count_nonzero(keep) == knum): break + knum = np.count_nonzero(keep) + + mesh.tria3 = mesh.tria3[keep] + + # delete unused vertices and reindex + ifwd = np.unique(mesh.tria3["index"].ravel()) + + irev = np.zeros(mesh.point.size, dtype=np.int32) + irev[ifwd] = np.arange(ifwd.size, dtype=np.int32) + + mesh.point = mesh.point[ifwd] + mesh.value = mesh.value[ifwd] + mesh.tria3["index"] = irev[mesh.tria3["index"]] + return mesh + +def write_gmsh_mesh(filename, node_data, tri): + num_nodes = node_data.shape[0] + + # Write the mesh to the file + with open(filename, 'w') as fileID: + fileID.write("$MeshFormat\n") + fileID.write("2 0 8\n") + fileID.write("$EndMeshFormat\n") + fileID.write("$Nodes\n") + fileID.write(str(num_nodes) + "\n") + + for i in range(num_nodes): + fileID.write( + f"{i + 1} {node_data[i, 0]:5.5f} {node_data[i, 1]:5.5f} {node_data[i, 2]:5.5f}\n" + ) + + fileID.write("$EndNodes\n") + fileID.write("$Elements\n") + num_elements = len(tri) + fileID.write(str(num_elements) + "\n") + + m = 0 + for i in range(len(tri)): + m += 1 + fileID.write(f"{m} 2 3 0 {i+1} 0 {tri[i][0]} {tri[i][1]} {tri[i][2]}\n") + + fileID.write("$EndElements\n") + +def interp_bathymetry_v0(): + + +#-- remap a DEM on to the vertices of the mesh + + print("*interp-batyhmetry...") + + # Load the DEM file from the config + dem_file = "../RWPS/Data/RTopo_2_0_4_GEBCO_v2023_60sec_pixel.nc" + + data = nc.Dataset(dem_file,"r") + + xlon = np.asarray(data["lon"][:]) + ylat = np.asarray(data["lat"][:]) + elev = np.asarray(data["bed_elevation"][:]) + \ + np.asarray(data["ice_thickness"][:]) + + xmid = 0.5 * (xlon[:-1:] + xlon[1::]) + ymid = 0.5 * (ylat[:-1:] + ylat[1::]) + + ffun = RegularGridInterpolator((ymid, xmid), elev,bounds_error=False, fill_value=None) + + vert = mesh.point["coord"] + hdown = ffun( (vert[:, 1], vert[:, 0]) ) + + vert[:,2]=hdown + mesh.point["coord"][:,2]=vert[:,2] + return mesh + + +def interp_bat(): + +#-- remap a DEM on to the vertices of the mesh + + print("*interp-batyhmetry...") + + ffun = RegularGridInterpolator((topo.ygrid, topo.xgrid),topo.value,bounds_error=False, fill_value=None) + vert = mesh.point["coord"] + hdown = ffun( (vert[:, 1], vert[:, 0]) ) + mesh.value=hdown + return mesh + +def do_nothing( ): + print("the end") + diff --git a/unst_msh_gen/regional/MeshGenTemplateDirectory/LonCon.m b/unst_msh_gen/regional/MeshGenTemplateDirectory/LonCon.m new file mode 100644 index 0000000..3a1b6ac --- /dev/null +++ b/unst_msh_gen/regional/MeshGenTemplateDirectory/LonCon.m @@ -0,0 +1,10 @@ +function lon=LonCon(lon) +% function lon=LonCon(lon) +% Transform longitude to match longitude convention you want to use +% here. This is used to avoid discontinuity at the international; +% dateline etc. + + +%lon=lon; +j=find(lon<90);lon(j)=lon(j)+360; % RWPS +%j=find(lon>180);lon(j)=lon(j)-360;% \ No newline at end of file diff --git a/unst_msh_gen/regional/MeshGenTemplateDirectory/MakeBoundaryForcingNodes.m b/unst_msh_gen/regional/MeshGenTemplateDirectory/MakeBoundaryForcingNodes.m new file mode 100644 index 0000000..2d4b424 --- /dev/null +++ b/unst_msh_gen/regional/MeshGenTemplateDirectory/MakeBoundaryForcingNodes.m @@ -0,0 +1,92 @@ +function MakeBoundaryForcingNodes(dx,CheckGFS) +if nargin<2,CheckGFS=0;end + +Blon=[129.91 10.71]; +Blon(1)=Blon(1)-360; +Blat=[-30.42 79.99]; +g=loadmshWW3('RWPS.OSMxGSHHS.GMU.txt.WW3.msh'); + +jb=unique(g.bnd); + +xb=g.x(jb); +yb=g.y(jb); + +%x=Blon(1)-dx:dx:Blon(2)+dx; +%y=Blat(1)-dx:dx:Blat(2)+dx; +x=Blon(1):dx:Blon(2); +y=Blat(1):dx:Blat(2); +if x(end)~=Blon(2), + x=[x,Blon(2)]; +end +if y(end)~=Blat(2), + y=[y,Blat(2)]; +end + + +Xs=x; +Ys=zeros(size(Xs))+min(Blat); +Xn=x(end:-1:1); +Yn=zeros(size(Xs))+max(Blat); + +Ye=y +Xe=zeros(size(Ye))+max(Blon); +Yw=y(end:-1:1); +Xw=zeros(size(Yw))+min(Blon); + +X=[Xs,Xe,Xn,Xw]; +Y=[Ys,Ye,Yn,Yw]; +whos X Y +clear m i; +for k=1:length(X); + m(k)=min(abs(X(k)+i*Y(k)-xb-i*yb)); +end + +j=find(m<2*dx); +Xp=X(j); +Yp=Y(j); +Xpp=Xp; +jW=find(Xpp<0); +Xpp(jW)=Xpp(jW)+360; + +filename = ['BoundaryDX',int2str(round(1/dx)),'thDeg.RWPS.txt'] +fileID = fopen(filename, 'w'); +fprintf(fileID, '%6f %6f\n',[Xpp(:),Yp(:)]'); +fclose(fileID); + +figure; +x=g.x;y=g.y;z=g.z;e=g.e; +clf;ph=patch(x(e'),y(e'),z(e'));shading interp;colormap('jet');colorbar; caxis([0,5000]);axis equal +hold on +plot(Xp,Yp,'k.-'); +plot(xb,yb,'r.') + +if CheckGFS, + Xpp=Xpp(:); + Yp=Yp(:); + xb=Xpp; + yb=Yp; + g=loadmshWW3('/mnt/sda/keston/meshes/uglo_15km.msh'); + x=g.x;y=g.y;z=g.z;e=g.e; + + clf;patch_global(x,y,z,e');shading interp; + cm=colormap('jet');colormap(flip(cm));colorbar; caxis([0,5000]);axis equal + + zb=scattered_interp2_noext(g.x,g.y,g.z,g.e,xb,yb) + j=find(isnan(zb )) + + nb=length(xb); + k=setdiff(1:nb,j) + hold on; + phg=plot(xb(k),yb(k),'w.'); + phb=plot(xb(j),yb(j),'ko',xb(j),yb(j),'kx'); + set(phb(1),'MarkerSize',13);set(phb(2),'MarkerSize',13); + title([int2str(round(1/dx)),'th degree spacing. White dot RWPS bound in GFS mesh, Black-Circle points out of GFS mesh ']); + kprint(['BoundaryDX',int2str(round(1/dx)),'thDeg.RWPS.jpg']) + InGFS=k; + OutGFS=j + + filename = ['BoundaryDX',int2str(round(1/dx)),'thDeg.RWPS.InGFS.txt'] + fileID = fopen(filename, 'w'); + fprintf(fileID, '%6f %6f\n',[Xpp(k),Yp(k)]'); + fclose(fileID); +end diff --git a/unst_msh_gen/regional/MeshGenTemplateDirectory/MakeCoastalBoundariesGSHHS.m b/unst_msh_gen/regional/MeshGenTemplateDirectory/MakeCoastalBoundariesGSHHS.m new file mode 100644 index 0000000..3dce92d --- /dev/null +++ b/unst_msh_gen/regional/MeshGenTemplateDirectory/MakeCoastalBoundariesGSHHS.m @@ -0,0 +1,141 @@ +function MakeCoastalBoundariesGSHHS + +% Script to use Global Self-consistent, Hierarchical, High-resolution Geography Database (GSHHG) to create global land boundaries +% Data available at https://www.ngdc.noaa.gov/mgg/shorelines/shorelines.html + +% make coastlines for mesh generation. +%Islands smaller than a threshold area are excluded. +%Narrow islands(some attols) with less than critical area are included if +%perimeter is longer than minPerimeter. Global and Pacific values are treated differently +%Coastline is smoothed and subsampled to usefull scale for mesh generation in RWPS +%various smoothings of coastlines + + +SetPath +isplot=0 +deg2km=111.132954 +deg2rad=pi/180 + + +%Pacific +minareaP=1;% square km +minPerimeterP=3;% km + +minareaG=1;% square km +minPerimeterG=10;% km + +geom.mshID='EUCLIDEAN-MESH' +geom.fileV = 3 + +%Use different island filtering with in Pacific rectangle: +PacLon=[-140,140] +PacLat=[-20,40]; + +earth=referenceSphere('Earth') + +%gcfl='/scratch3/NCEPDEV/climate/Keston.Smith/RWPS/Data/GlobalCoast/GSHHS_shp/f/GSHHS_f_L1.shp' +S = shaperead(gcfl); +N=length(S); +isisland=zeros(N,1); +for k=1:N + ns(k)=length(S(k).X(1:end-1)); + if and( S(k).X(end-1)==S(k).X(1) , S(k).Y(end-1)==S(k).Y(1) ) + isisland(k)=1; + end +end +if sum(isisland)==N + disp(['All features in ',gcfl,' are closed islands']) +else + disp([int2str(sum(isisland)),' features in ',gcfl,... + ' are closed islands. out of:',int2str(N),' total features']) +end + +%S0=S;%??? +n=0; +for k=1:N + x=S(k).X(1:end-1); + y=S(k).Y(1:end-1); +% [xs,ys]=SmoothSubSampleCoastlineFast(x,y,100.,75); + [xs,ys]=SmoothSubSampleCoastlineFast(x,y,50.,10);%500 m coastline + if length(xs)>2, + n=n+1; + S0(n).X=[xs(:);NaN]'; + S0(n).Y=[ys(:);NaN]'; + S0(n).X0=S(n).X; + S0(n).Y0=S(n).Y; + S0(n).area = areaint(ys,xs,earth) / 10^6; + S0(n).perim=sum( deg2km*abs( cos(deg2rad*mean(ys))*( xs(2:end)-xs(1:end-1) ) + i*(ys(2:end)-ys(1:end-1)) ) ); + S0(n).Geometry=S(k).Geometry; + end + + if mod(k,1000)==0, + disp(['progress a:', num2str( sum(ns(1:k))/sum(ns) )]); + disp(['progress b:', num2str( k/N )]); + end +end + +N=length(S0) +for k=1:N + ns0(k)=length(S0(k).X(1:end-1)); +end +S=S0; + +save -v7.3 GlobalCoastlineGSHHS.mat S ns0 +%filter out small islands +js=[154,53]; +jsp=[154,53]; +clear A P +for n=1:length(S), + xp=mean(S(n).X(1:end-1)); + yp=mean(S(n).Y(1:end-1)); + if and( or(xpPacLon(2)), and(yp>PacLat(1),ypminarea, + js=[js,n]; + end + %if isempty(S(n).perim),S(n).perim=0;end + P(n)=S(n).perim; + if S(n).perim>minperim, + jsp=[jsp,n]; % perimeter criteria for narrow Atolls + end + Amin(n)=minarea; + Pmin(n)=minperim; + if mod(n,1000)==0, + disp(['progress ', num2str( n/ length(S) )]); + end + +end + +close all +if isplot + figure; + for k=1:length(js) + n=js(k); + xs=S(n).X(1:end-1); + ys=S(n).Y(1:end-1); + plot(xs,ys,'k-');hold on + end + pindx=setdiff(jsp,js) % narrow attols + for k=1:length(pindx) + n=pindx(k); + xs=S(n).X(1:end-1); + ys=S(n).Y(1:end-1); + plot(xs,ys,'r.-'); + end +end +jsg=union(js,jsp); + +%js=find(A>minarea); +S=S(jsg); +save -v7.3 GlobalCoastlineGSHHS.mat S +S=rmfield(S,'X0') +S=rmfield(S,'Y0') + +%BoundaryShape2msh(S,'GlobalCoastlineGSHHS.msh'); +shapewrite(S, 'GlobalCoastlineGSHHS.shp'); diff --git a/unst_msh_gen/regional/MeshGenTemplateDirectory/MakeCoastalBoundariesOSM.m b/unst_msh_gen/regional/MeshGenTemplateDirectory/MakeCoastalBoundariesOSM.m new file mode 100644 index 0000000..aa2539d --- /dev/null +++ b/unst_msh_gen/regional/MeshGenTemplateDirectory/MakeCoastalBoundariesOSM.m @@ -0,0 +1,183 @@ +function MakeCoastalBoundariesOSM + +% Script to use Open Street Map(OSM) coastline to create global land boundaries +% https://osmdata.openstreetmap.de/download/land-polygons-complete-4326.zip + +%Islands smaller than a threshold area are excluded. +%Narrow islands(some atolls) with less than critical area are included if +%perimeter is longer than minPerimeter. Global and Pacific values are treated differently +%Coastline is smoothed and subsampled to usefull scale for mesh generation in RWPS +%various smoothings of coastlines + +SetPath +isplot=0 +deg2km=111.132954 +deg2rad=pi/180 + + +%Pacific +minareaP=1;% square km +minPerimeterP=3;% km + +minareaG=1;% square km +minPerimeterG=10;% km + +geom.mshID='EUCLIDEAN-MESH' +geom.fileV = 3 +%filter out small islands +PacLon=[-140,140] +PacLat=[-20,40]; + +earth=referenceSphere('Earth') + +%gcfl='/scratch3/NCEPDEV/climate/Keston.Smith/RWPS/Data/openstreetmap_land/land_polygons.shp' + +S = shaperead(gcfl); +N=length(S); +isisland=zeros(N,1); +for k=1:N + ns(k)=length(S(k).X(1:end-1)); + if and( S(k).X(end-1)==S(k).X(1) , S(k).Y(end-1)==S(k).Y(1) ) + isisland(k)=1; + end +end +if sum(isisland)==N + disp(['All features in ',gcfl,' are closed islands']) +else + disp([int2str(sum(isisland)),' features in ',gcfl,... + ' are closed islands. out of:',int2str(N),' total features']) +end + +for k=1:N + x=S(k).BoundingBox(:,1); + y=S(k).BoundingBox(:,2); + x0(k)=mean(x); + y0(k)=mean(y); + dx(k)=abs(x(1)-x(2)); + dy(k)=abs(y(1)-y(2)); +end +A=[dx.*cos(deg2rad*y0/180)*deg2km].*[dy*deg2km]; +k=find(A>.500*.500); +S=S(k); +ns=ns(k); +N=length(S); +[ss,k]=sort(-ns); +S=S(k); +ns=ns(k); +N=length(S); +n=0; + +for k=1:N + x=S(k).X(1:end-2);%unique* points + y=S(k).Y(1:end-2); +%deal with "messy" OpenStreetMap coasts + jb=find(isnan(x+y)); + if ~isempty(jb) + x=x(1:[min(jb)-1]); + y=y(1:[min(jb)-1]);%truncate coastline at first discontinuity + end + zz=x+i*y; + [zzu,j]=unique(zz);%find unique points before end + j=sort(j); + if length(zzu)2 + x=[x,x(1)];%close loop + y=[y,y(1)]; + [xs,ys]=SmoothSubSampleCoastlineFast(x,y,50.,10);%500 m coastline + + zs=xs+i*ys; + dz=abs(zs(2:end)-zs(1:end-1)); + j=find(dz>1); + if length(j)>0 + ji=1:j(1); + xs=xs(ji);ys=ys(ji); + end + + if length(xs)>2, + n=n+1; + S0(n).X=[xs(:);NaN]'; + S0(n).Y=[ys(:);NaN]'; + S0(n).X0=S(n).X; + S0(n).Y0=S(n).Y; + S0(n).area = areaint(ys,xs,earth) / 10^6; + S0(n).perim=sum( deg2km*abs( cos(deg2rad*mean(ys))*( xs(2:end)-xs(1:end-1) ) + i*(ys(2:end)-ys(1:end-1)) ) ); + S0(n).Geometry=S(k).Geometry; + end + end + + if mod(k,1000)==0, + disp(['progress a:', num2str( sum(ns(1:k)) / sum(ns) ), ', progress b:', num2str( k/N )]); + end +end + +N=length(S0) +for k=1:N + ns0(k)=length(S0(k).X(1:end-1)); +end +S=S0; + +save -v7.3 GlobalCoastlineOSM.mat S ns0 +%filter out small islands +js=[ ]; +jsp=[ ]; +clear A P +for n=1:length(S), + xp=mean(S(n).X(1:end-1)); + yp=mean(S(n).Y(1:end-1)); + if and( or(xpPacLon(2)), and(yp>PacLat(1),ypminarea, + js=[js,n]; + end + %if isempty(S(n).perim),S(n).perim=0;end + P(n)=S(n).perim; + if S(n).perim>minperim, + jsp=[jsp,n]; % perimeter criteria for narrow Atolls + end + Amin(n)=minarea; + Pmin(n)=minperim; + if mod(n,1000)==0, + disp(['progress ', num2str( n/ length(S) )]); + end + +end + +if isplot + + close all + figure; + for k=1:length(js) + n=js(k); + xs=S(n).X(1:end-1); + ys=S(n).Y(1:end-1); + plot(xs,ys,'k-');hold on + end + pindx=setdiff(jsp,js) % narrow attols + for k=1:length(pindx) + n=pindx(k); + xs=S(n).X(1:end-1); + ys=S(n).Y(1:end-1); + plot(xs,ys,'r.-'); + end +end + +jsg=union(js,jsp); + +%js=find(A>minarea); +S=S(jsg); +save -v7.3 GlobalCoastlineOSM.mat S +S=rmfield(S,'X0') +S=rmfield(S,'Y0') + +%BoundaryShape2msh(S,'GlobalCoastlineOSM.msh'); +shapewrite(S, 'GlobalCoastlineOSM.shp'); diff --git a/unst_msh_gen/regional/MeshGenTemplateDirectory/MakeDfunGSHHS.m b/unst_msh_gen/regional/MeshGenTemplateDirectory/MakeDfunGSHHS.m new file mode 100644 index 0000000..f04d0eb --- /dev/null +++ b/unst_msh_gen/regional/MeshGenTemplateDirectory/MakeDfunGSHHS.m @@ -0,0 +1,312 @@ +%This script creates a "Distance to Coast file" using a US coastline file +% and custom specification of other designated points. The PSLG intended for creation of the boundary +% is used as well as a global bathymetry file and US coastline shapefile. + +clear +close all +isplot=0 + +%Input files +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%PSLGfile='GlobalCoastlineGSHHS.PSLG.msh' +%PSLGfile='GlobalCoastlineOSM.PSLG.msh' +%PSLGfile='PSLGboundaryOSMxGSHHS.BOXES.msh' + +%Coastline Boundary file +PSLGfile='GlobalCoastlineGSHHS.PSLG.msh' +%Global Bathymetry file (netcdf) +GlobalTopoFile='/scratch3/NCEPDEV/climate/Keston.Smith/RWPS/Data/RTopo_2_0_4_GEBCO_v2023_60sec_pixel.nc' +%Shape file with US coastline +uscl='/scratch3/NCEPDEV/climate/Keston.Smith/RWPS/Data/us_coastline/tl_2023_us_coastline.shp' + +%Output files +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%Output "distance to coast" grided jigsaw .msh file +DFunOutFile=['DFun.',PSLGfile(1:end-4),'.msh'] +%Output bathymetry on same grid as DFunOutFile +TopoFile=['Topo.',DFunOutFile]; +%Temporary matlab file +FileOutMatlab=[PSLGfile(1:end-4),'.MakeDistance.mat'] + + +US=shaperead(uscl); +NUS=length(US) +xus=[]; +yus=[]; +x0=[] +y0=[]; +interpDist=100. %meters +SmoothN=25;% box car smooth coastline at width 2(*SmoothN+1)*interpDist +% then subsample soothed coastline every SmoothN points to create smaller +% coastline data set. i.e. remove small scale variation +for k=1:NUS + x=US(k).X(1:end-1); + y=US(k).Y(1:end-1); + x0=[x0,x]; + y0=[y0,y]; +% xs=x;ys=y; %1.5 mil points + if length(x)>3 + [xs,ys]=SmoothSubSampleCoastlineFast(x,y,interpDist,SmoothN); + xus=[xus,xs]; + yus=[yus,ys]; + end +end +n0=length(xus); +%Now add Pacific Territories and COFA points +%------------------------------------------------------------------------ +%NWS Pacific Region, via the Compact of Free Association, +% %oversees operations of 5 Weather Service Offices (WSO) across +% Micronesia in the Republic of Palau, Federated States of Micronesia +% and the Republic of the Marshall Islands. WFO Guam provides routine +% forecasts as well as WWA services for these areas. -Eric Lau +%Palau,Yap, Chuuk, Pohnpei, Majuro, Pago Pago, Wake island + +%Wake Island +coord=[19.2796,166.6499];%E +xus=[xus,coord(2)];yus=[yus,coord(1)]; +%Palau +coord=[7.4942, 134.5690] +xus=[xus,coord(2)];yus=[yus,coord(1)]; +%Yap:9.5557° N, 138.1399° E +coord=[9.5557, 138.1399] +xus=[xus,coord(2)];yus=[yus,coord(1)]; +%Chuuk: 7°25′N 151°47′E +coord=[7.374227, 151.754606]%E +xus=[xus,coord(2)];yus=[yus,coord(1)]; +%Pohnpei › Coordinates: 6.8519° N, 158.2147° E +coord=[6.8519, 158.2147] +xus=[xus,coord(2)];yus=[yus,coord(1)]; +%Majuro › Coordinates7.0667° N, 171.2667° E +coord=[7.0667, 171.2667] +xus=[xus,coord(2)];yus=[yus,coord(1)]; +%Pago Pago › Coordinates: 14.2732° S, 170.7030° W +coord=[-14.2732, -170.7030]% SW +xus=[xus,coord(2)];yus=[yus,coord(1)]; +% Kosrae › Coordinates :5.3096° N, 162.9815° E +coord=[5.3096, 162.9815] +xus=[xus,coord(2)];yus=[yus,coord(1)]; +%------------------------------------------------------------------------ + +%Marshall Islands +%Majuro +coord=[7.0667, 171.2667] +%xus=[xus,coord(2)];yus=[yus,coord(1)]; +%Ebeye +coord=[8.7815, 167.7373] +%xus=[xus,coord(2)];yus=[yus,coord(1)]; +%Micronesia +%Kolonia, +coord=[6.9636, 158.2102] +%xus=[xus,coord(2)];yus=[yus,coord(1)]; +%Pohnpei, +coord=[6.8519, 158.2147] +%xus=[xus,coord(2)];yus=[yus,coord(1)]; +% Chuuk-Weno +coord=[7.4523, 151.8422] +%xus=[xus,coord(2)];yus=[yus,coord(1)]; +% Tofol +coord=[5.3256, 163.0086] +%xus=[xus,coord(2)];yus=[yus,coord(1)]; +%Colonia -between Palau and Guam +coord=[9.5164,138.1222] +%xus=[xus,coord(2)];yus=[yus,coord(1)]; +n1=length(xus); +%------------------------------------------------------------------------ +%Points from Curt +%Howland Island -Baker +coord=[0.8113, -176.6183]%W +xus=[xus,coord(2)];yus=[yus,coord(1)]; +%Johnston atoll +coord=[16.7295, -169.5336]%W +xus=[xus,coord(2)];yus=[yus,coord(1)]; +%Palmyra +coord=[5.8885, -162.0787]%W +xus=[xus,coord(2)];yus=[yus,coord(1)]; +%Jarvis Island +coord=[0.3744, -159.9967]%W +xus=[xus,coord(2)];yus=[yus,coord(1)]; +%Baker Island +%0.1936° N, 176.4769° W +coord=[0.1936,-176.4769 ]%W +xus=[xus,coord(2)];yus=[yus,coord(1)]; +%------------------------------------------------------------------------ + coord=[18.4101, -75.0115 ]%W + xus=[xus,coord(2)];yus=[yus,coord(1)]; + %If your team is going to the effort to add resolution for Majuro in RMI, I would strongly suggest doing the same for these atolls in RMI: + %1. Kwajalein Atoll, which is home to part of the Ronald Regard Ballistic Missile Test Site (https://home.army.mil/kwajalein/index.php) and the underprediction of wave heights by NWS' WaveWatchIII model in January 2024, which I discussed on our last call, is what led to significant damage at the base, per: https://www.youtube.com/shorts/jH-pGoQDdcg + %2. Enewetak Atoll, which is the home of the Runit Dome (https://en.wikipedia.org/wiki/Runit_Island), is threatened by wave-driven overwash that has serious implications for the US Department of State, Department of Defense/War, and Department of the Interior via the Intergovernmental Compact of Free Association (https://www.doi.gov/oia/compacts-of-free-association). + %3. Bikini Atoll, for similar reasons as Enewetak, although the radionuclides are all over the place and not all dumped in one location. + %I would note that most of the atolls drop off at a 70-80 degree slope from approximately 30 m depth (which is generally less than 1000 m from shore) to over 1000 m depth, so there is not a need for a large region of increasing resolution to capture a broad continental shelf as characterizes CONUS. + + %Kwajalein Atoll - 9.1898° N, 167.4243° E + %Enewetak Atoll - 11.4654° N, 162.1890° E + %Bikini Atoll - 11.6065° N, 165.3768° E + + %Kwajalein Atoll - 9.1898° N, 167.4243° E + coord=[9.1898, 167.4243 ]%W + xus=[xus,coord(2)];yus=[yus,coord(1)]; + %Enewetak Atoll - 11.4654° N, 162.1890° E + coord=[ 11.4654, 162.1890]%W + xus=[xus,coord(2)];yus=[yus,coord(1)]; + %Bikini Atoll - 11.6065° N, 165.3768° E + coord=[11.6065, 165.3768 ]%W + xus=[xus,coord(2)];yus=[yus,coord(1)]; + +Blon=[129.91 10.71]; +Blat=[-30.42 79.99]; +lon=Blon;j=find(lon<90);lon(j)=lon(j)+360; +Blon=lon; +xusp=xus;j=find(xus>0);xusp(j)=xus(j)-360; + +if isplot, + clf; + plot(xusp(1:n0) ,yus(1:n0),'k.',... + xusp(n0+1:n1) ,yus(n0+1:n1),'co', ... + xusp(n1+1:end),yus(n1+1:end),'rx'); + hold on + BoundingBox(Blon-360,Blat,'r'); + grid on; + title('Updated RWPS high res target points'); + kprint('RWPSHighResPointsX.jpg'); +end +topo=BoxSmoothTopo(GlobalTopoFile,2); + +lon = topo.point.coord{:,1}; +lat = topo.point.coord{:,2}; + +%p=loadmsh('PSLGboundaryOSMxGSHHS1km.msh'); +p=loadmsh(PSLGfile); + +p.x=p.point.coord(:,1)-360; +p.y=p.point.coord(:,2); +p.edges=p.edge2.index(:,1:2); +plot(p.x,p.y,'g.'); + +%Remove near boundary points- Not land targets of resolution +dx=1; +j=find(p.xmin(p.x)+dx);p=subpslgFast(p,j); +j=find(p.ymin(p.y)+dx);p=subpslgFast(p,j); +plot(p.x,p.y,'y.'); + +j=find(xus>90); +xus(j)=xus(j)-360; + +%This loop Takes a few min:Find PSLG points near us coastline +deg2km=111.132954 +np=length(p.x); +NP=10000; + +clear d; +nus=length(xus); +for k=1:NP:np + j=k:min(np,k+NP); + x=p.x(j); + y=p.y(j); + d(j)=min(deg2km*abs([x(:)-xus(:)'].*[cos(pi*y(:)/180)*ones(1,nus)]+i*[y(:)-yus(:)'])'); + %if mod(k,1000)==0,k/np,end + k/np +end + +dmin=100; +%dmin=50; +j=find(d90); +xus(j)=xus(j)-360; +Blon=[129.91-360, 10.71]; +Blat=[-30.42, 79.99]; +if isplot, + plot(p.x,p.y,'k.'); + hold on + BoundingBox(Blon,Blat,'k'); + th=title('PSLG defining boundary'); + set(th,'FontSize',22); + kprint('PSLG.jpg') + + plot(xus,yus,'r.'); + th=title('PSLG with US coasline data points(red)'); + set(th,'FontSize',22); + kprint('PSLGwUSpoints.jpg') + + j=find(d<100); + plot(p.x(j),p.y(j),'c.'); + th=title('PSLG with US coasline data points and PSLG points near us (cyan)'); + set(th,'FontSize',22); + kprint('PSLGwUSpointsPSLGnear.jpg') +end + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%add points to off shore banks we want to refine here we have Georges Bank +%and banks around the Bahamas. + +xFB= [ -79.9909 -78.1963 -78.3957] +yFB= [23.7978 26.8928 24.1530] +xGB = -67.4517 +yGB = 41.3567 + +xx=[xUSsl(:);xus(:);xFB(:);xGB(:)]'; +yy=[yUSsl(:);yus(:);yFB(:);yGB(:)]'; +j=find(xx>90); +xx(j)=xx(j)-360; +plot(xx,yy,'c.'); + +D=DistanceToCoast(lon,lat,xx,yy); + +eval(['save -v7.3 ',FileOutMatlab, ' lon lat D xx yy xus yus xUSsl yUSsl']) ; + + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Make .msh files for jigsaw + +topo=BoxSmoothTopo(GlobalTopoFile,2); + +Dfun=topo; + +lon=Dfun.point.coord{:,1}; +j=find(lon<90);lon(j)=lon(j)+360; +j0=setdiff(1:length(lon),j);j0=j0(:); +lon=lon([j0(:);j(:)]); +D1=[D(:,j0),D(:,j)]; +Dfun.point.coord{:,1}=lon; +Dfun.value=D1; + +Dmax=20004000%max distance between two points on earth +D1(find(D1>Dmax))=Dmax; +Dfun.value=D1; + +figure;clf;pcolor(Dfun.point.coord{:,1},Dfun.point.coord{:,2},exp(-Dfun.value/320000)); +shading interp;colorbar;colormap('jet') +savemsh(DFunOutFile,Dfun); + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% save smoothed topo at same resolution as Distance function +topo=BoxSmoothTopo(GlobalTopoFile,2); + +D=topo.value; +lon=topo.point.coord{:,1}; +j=find(lon<90);lon(j)=lon(j)+360; +j0=setdiff(1:length(lon),j);j0=j0(:); +lon=lon([j0(:);j(:)]); +D1=[D(:,j0),D(:,j)]; +topo.point.coord{:,1}=lon; +topo.value=D1; + +figure;clf;pcolor(topo.point.coord{:,1},topo.point.coord{:,2},topo.value); +shading interp;colorbar;colormap('jet') +savemsh(TopoFile,topo); + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/unst_msh_gen/regional/MeshGenTemplateDirectory/MakeDfunOSM.m b/unst_msh_gen/regional/MeshGenTemplateDirectory/MakeDfunOSM.m new file mode 100644 index 0000000..b665fa0 --- /dev/null +++ b/unst_msh_gen/regional/MeshGenTemplateDirectory/MakeDfunOSM.m @@ -0,0 +1,315 @@ +%This script creates a "Distance to Coast file" using a US coastline file +% and custom specification of other designated points. The PSLG intended for creation of the boundary +% is used as well as a global bathymetry file and US coastline shapefile. + +%clear +SetPath +close all +isplot=0 + +%Input files +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%Coastline Boundary file +PSLGfile='GlobalCoastlineOSM.PSLG.msh' +%Global Bathymetry file (netcdf) +%GlobalTopoFile='/scratch3/NCEPDEV/climate/Keston.Smith/RWPS/Data/RTopo_2_0_4_GEBCO_v2023_60sec_pixel.nc' +%GlobalTopoFile='/scratch3/NCEPDEV/climate/Keston.Smith/RWPS/Data/RTopo_2_0_4_GEBCO_v2024_60sec_pixel.nc' +%GlobalTopoFile='../Data/Bathymetry/RTopo_2_0_4_GEBCO_v2024_60sec_pixel.nc' +GlobalTopoFile, %set as a global variable in SetPath +%Shape file with US coastline +%uscl='/scratch3/NCEPDEV/climate/Keston.Smith/RWPS/Data/us_coastline/tl_2023_us_coastline.shp' +uscl, %set as a global variable in SetPath + +%Output files +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%Output "distance to coast" grided jigsaw .msh file +DFunOutFile=['DFun.',PSLGfile(1:end-4),'.msh'] +%Output bathymetry on same grid as DFunOutFile +TopoFile=['Topo.',DFunOutFile]; +%Temporary matlab file +FileOutMatlab=[PSLGfile(1:end-4),'.MakeDistance.mat'] + + +US=shaperead(uscl); +NUS=length(US) +xus=[]; +yus=[]; +x0=[] +y0=[]; +interpDist=100. %meters +SmoothN=25;% box car smooth coastline at width 2(*SmoothN+1)*interpDist +% then subsample soothed coastline every SmoothN points to create smaller +% coastline data set. i.e. remove small scale variation +for k=1:NUS + x=US(k).X(1:end-1); + y=US(k).Y(1:end-1); + x0=[x0,x]; + y0=[y0,y]; +% xs=x;ys=y; %1.5 mil points + if length(x)>3 + [xs,ys]=SmoothSubSampleCoastlineFast(x,y,interpDist,SmoothN); + xus=[xus,xs]; + yus=[yus,ys]; + end +end +n0=length(xus); +%Now add Pacific Territories and COFA points +%------------------------------------------------------------------------ +%NWS Pacific Region, via the Compact of Free Association, +% %oversees operations of 5 Weather Service Offices (WSO) across +% Micronesia in the Republic of Palau, Federated States of Micronesia +% and the Republic of the Marshall Islands. WFO Guam provides routine +% forecasts as well as WWA services for these areas. -Eric Lau +%Palau,Yap, Chuuk, Pohnpei, Majuro, Pago Pago, Wake island + +%Wake Island +coord=[19.2796,166.6499];%E +xus=[xus,coord(2)];yus=[yus,coord(1)]; +%Palau +coord=[7.4942, 134.5690] +xus=[xus,coord(2)];yus=[yus,coord(1)]; +%Yap:9.5557° N, 138.1399° E +coord=[9.5557, 138.1399] +xus=[xus,coord(2)];yus=[yus,coord(1)]; +%Chuuk: 7°25′N 151°47′E +coord=[7.374227, 151.754606]%E +xus=[xus,coord(2)];yus=[yus,coord(1)]; +%Pohnpei › Coordinates: 6.8519° N, 158.2147° E +coord=[6.8519, 158.2147] +xus=[xus,coord(2)];yus=[yus,coord(1)]; +%Majuro › Coordinates7.0667° N, 171.2667° E +coord=[7.0667, 171.2667] +xus=[xus,coord(2)];yus=[yus,coord(1)]; +%Pago Pago › Coordinates: 14.2732° S, 170.7030° W +coord=[-14.2732, -170.7030]% SW +xus=[xus,coord(2)];yus=[yus,coord(1)]; +% Kosrae › Coordinates :5.3096° N, 162.9815° E +coord=[5.3096, 162.9815] +xus=[xus,coord(2)];yus=[yus,coord(1)]; +%------------------------------------------------------------------------ + +%Marshall Islands +%Majuro +coord=[7.0667, 171.2667] +%xus=[xus,coord(2)];yus=[yus,coord(1)]; +%Ebeye +coord=[8.7815, 167.7373] +%xus=[xus,coord(2)];yus=[yus,coord(1)]; +%Micronesia +%Kolonia, +coord=[6.9636, 158.2102] +%xus=[xus,coord(2)];yus=[yus,coord(1)]; +%Pohnpei, +coord=[6.8519, 158.2147] +%xus=[xus,coord(2)];yus=[yus,coord(1)]; +% Chuuk-Weno +coord=[7.4523, 151.8422] +%xus=[xus,coord(2)];yus=[yus,coord(1)]; +% Tofol +coord=[5.3256, 163.0086] +%xus=[xus,coord(2)];yus=[yus,coord(1)]; +%Colonia -between Palau and Guam +coord=[9.5164,138.1222] +%xus=[xus,coord(2)];yus=[yus,coord(1)]; +n1=length(xus); +%------------------------------------------------------------------------ +%Points from Curt +%Howland Island -Baker +coord=[0.8113, -176.6183]%W +xus=[xus,coord(2)];yus=[yus,coord(1)]; +%Johnston atoll +coord=[16.7295, -169.5336]%W +xus=[xus,coord(2)];yus=[yus,coord(1)]; +%Palmyra +coord=[5.8885, -162.0787]%W +xus=[xus,coord(2)];yus=[yus,coord(1)]; +%Jarvis Island +coord=[0.3744, -159.9967]%W +xus=[xus,coord(2)];yus=[yus,coord(1)]; +%Baker Island +%0.1936° N, 176.4769° W +coord=[0.1936,-176.4769 ]%W +xus=[xus,coord(2)];yus=[yus,coord(1)]; +%------------------------------------------------------------------------ + coord=[18.4101, -75.0115 ]%W + xus=[xus,coord(2)];yus=[yus,coord(1)]; + %If your team is going to the effort to add resolution for Majuro in RMI, I would strongly suggest doing the same for these atolls in RMI: + %1. Kwajalein Atoll, which is home to part of the Ronald Regard Ballistic Missile Test Site (https://home.army.mil/kwajalein/index.php) and the underprediction of wave heights by NWS' WaveWatchIII model in January 2024, which I discussed on our last call, is what led to significant damage at the base, per: https://www.youtube.com/shorts/jH-pGoQDdcg + %2. Enewetak Atoll, which is the home of the Runit Dome (https://en.wikipedia.org/wiki/Runit_Island), is threatened by wave-driven overwash that has serious implications for the US Department of State, Department of Defense/War, and Department of the Interior via the Intergovernmental Compact of Free Association (https://www.doi.gov/oia/compacts-of-free-association). + %3. Bikini Atoll, for similar reasons as Enewetak, although the radionuclides are all over the place and not all dumped in one location. + %I would note that most of the atolls drop off at a 70-80 degree slope from approximately 30 m depth (which is generally less than 1000 m from shore) to over 1000 m depth, so there is not a need for a large region of increasing resolution to capture a broad continental shelf as characterizes CONUS. + + %Kwajalein Atoll - 9.1898° N, 167.4243° E + %Enewetak Atoll - 11.4654° N, 162.1890° E + %Bikini Atoll - 11.6065° N, 165.3768° E + + %Kwajalein Atoll - 9.1898° N, 167.4243° E + coord=[9.1898, 167.4243 ]%W + xus=[xus,coord(2)];yus=[yus,coord(1)]; + %Enewetak Atoll - 11.4654° N, 162.1890° E + coord=[ 11.4654, 162.1890]%W + xus=[xus,coord(2)];yus=[yus,coord(1)]; + %Bikini Atoll - 11.6065° N, 165.3768° E + coord=[11.6065, 165.3768 ]%W + xus=[xus,coord(2)];yus=[yus,coord(1)]; + +Blon=[129.91 10.71]; +Blat=[-30.42 79.99]; +Blon=LonCon(Blon) + +xusp=LonCon(xus); +if isplot, + clf; + plot(xusp(1:n0) ,yus(1:n0),'k.',... + xusp(n0+1:n1) ,yus(n0+1:n1),'co', ... + xusp(n1+1:end),yus(n1+1:end),'rx'); + hold on + BoundingBox(Blon,Blat,'r'); + grid on; + title('Updated RWPS high res target points'); + kprint('RWPSHighResPointsX.jpg'); +end +topo=BoxSmoothTopo(GlobalTopoFile,2); + +lon = topo.point.coord{:,1}; +lat = topo.point.coord{:,2}; + +%p=loadmsh('PSLGboundaryOSMxGSHHS1km.msh'); +p=loadmsh(PSLGfile); + +%p.x=p.point.coord(:,1)-360; +p.x=LonCon(p.x); +p.y=p.point.coord(:,2); +p.edges=p.edge2.index(:,1:2); +plot(p.x,p.y,'g.'); + +%Remove near boundary points- Not land targets of resolution +dx=1; +j=find(p.xmin(p.x)+dx);p=subpslgFast(p,j); +j=find(p.ymin(p.y)+dx);p=subpslgFast(p,j); +plot(p.x,p.y,'y.'); + +%j=find(xus>90); +%xus(j)=xus(j)-360; +xus=LonCon(xus) + +%This loop Takes a few min:Find PSLG points near us coastline +deg2km=111.132954 +np=length(p.x); +NP=10000; + +clear d; +nus=length(xus); +for k=1:NP:np + j=k:min(np,k+NP); + x=p.x(j); + y=p.y(j); + d(j)=min(deg2km*abs([x(:)-xus(:)'].*[cos(pi*y(:)/180)*ones(1,nus)]+i*[y(:)-yus(:)'])'); + %if mod(k,1000)==0,k/np,end + k/np +end + +dmin=100; +%dmin=50; +j=find(d90); +xus(j)=xus(j)-360; +Blon=[129.91-360, 10.71]; +Blat=[-30.42, 79.99]; +if isplot, + plot(p.x,p.y,'k.'); + hold on + BoundingBox(Blon,Blat,'k'); + th=title('PSLG defining boundary'); + set(th,'FontSize',22); + kprint('PSLG.jpg') + + plot(xus,yus,'r.'); + th=title('PSLG with US coasline data points(red)'); + set(th,'FontSize',22); + kprint('PSLGwUSpoints.jpg') + + j=find(d<100); + plot(p.x(j),p.y(j),'c.'); + th=title('PSLG with US coasline data points and PSLG points near us (cyan)'); + set(th,'FontSize',22); + kprint('PSLGwUSpointsPSLGnear.jpg') +end + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%add points to off shore banks we want to refine here we have Georges Bank +%and banks around the Bahamas. + +xFB= [ -79.9909 -78.1963 -78.3957] +yFB= [23.7978 26.8928 24.1530] +xGB = -67.4517 +yGB = 41.3567 + +xx=[xUSsl(:);xus(:);xFB(:);xGB(:)]'; +yy=[yUSsl(:);yus(:);yFB(:);yGB(:)]'; +j=find(xx>90); +xx(j)=xx(j)-360; +plot(xx,yy,'c.'); + +D=DistanceToCoast(lon,lat,xx,yy); + +eval(['save -v7.3 ',FileOutMatlab, ' lon lat D xx yy xus yus xUSsl yUSsl']) ; + + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Make .msh files for jigsaw + +topo=BoxSmoothTopo(GlobalTopoFile,2); + +Dfun=topo; + +lon=Dfun.point.coord{:,1}; +j=find(lon<90);lon(j)=lon(j)+360; +j0=setdiff(1:length(lon),j);j0=j0(:); +lon=lon([j0(:);j(:)]); +D1=[D(:,j0),D(:,j)]; +Dfun.point.coord{:,1}=lon; +Dfun.value=D1; + +Dmax=20004000%max distance between two points on earth +D1(find(D1>Dmax))=Dmax; +Dfun.value=D1; + +figure;clf;pcolor(Dfun.point.coord{:,1},Dfun.point.coord{:,2},exp(-Dfun.value/320000)); +shading interp;colorbar;colormap('jet') +savemsh(DFunOutFile,Dfun); + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% save smoothed topo at same resolution as Distance function +topo=BoxSmoothTopo(GlobalTopoFile,2); + +D=topo.value; +lon=topo.point.coord{:,1}; +j=find(lon<90);lon(j)=lon(j)+360; +j0=setdiff(1:length(lon),j);j0=j0(:); +lon=lon([j0(:);j(:)]); +D1=[D(:,j0),D(:,j)]; +topo.point.coord{:,1}=lon; +topo.value=D1; + +figure;clf;pcolor(topo.point.coord{:,1},topo.point.coord{:,2},topo.value); +shading interp;colorbar;colormap('jet') +savemsh(TopoFile,topo); + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/unst_msh_gen/regional/MeshGenTemplateDirectory/MakeDfunOSMxGSHHS.m b/unst_msh_gen/regional/MeshGenTemplateDirectory/MakeDfunOSMxGSHHS.m new file mode 100644 index 0000000..68bf254 --- /dev/null +++ b/unst_msh_gen/regional/MeshGenTemplateDirectory/MakeDfunOSMxGSHHS.m @@ -0,0 +1,311 @@ +%This script creates a "Distance to Coast file" using a US coastline file +% and custom specification of other designated points. The PSLG intended for creation of the boundary +% is used as well as a global bathymetry file and US coastline shapefile. + +clear +close all +isplot=0 + +%Input files +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%Coastline Boundary file +PSLGfile='PSLGboundaryOSMxGSHHS.BOXES.msh' +%Global Bathymetry file (netcdf) +%GlobalTopoFile='/scratch3/NCEPDEV/climate/Keston.Smith/RWPS/Data/RTopo_2_0_4_GEBCO_v2023_60sec_pixel.nc' +%GlobalTopoFile='/scratch3/NCEPDEV/climate/Keston.Smith/RWPS/Data/RTopo_2_0_4_GEBCO_v2024_60sec_pixel.nc' +GlobalTopoFile='../Data/Bathymetry/RTopo_2_0_4_GEBCO_v2024_60sec_pixel.nc' +%Shape file with US coastline +uscl='/scratch3/NCEPDEV/climate/Keston.Smith/RWPS/Data/us_coastline/tl_2023_us_coastline.shp' + +%Output files +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%Output "distance to coast" grided jigsaw .msh file +DFunOutFile=['DFun.',PSLGfile(1:end-4),'.msh'] +%Output bathymetry on same grid as DFunOutFile +TopoFile=['Topo.',DFunOutFile]; +%Temporary matlab file +FileOutMatlab=[PSLGfile(1:end-4),'.MakeDistance.mat'] + + +US=shaperead(uscl); +NUS=length(US) +xus=[]; +yus=[]; +x0=[] +y0=[]; +interpDist=100. %meters +SmoothN=25;% box car smooth coastline at width 2(*SmoothN+1)*interpDist +% then subsample soothed coastline every SmoothN points to create smaller +% coastline data set. i.e. remove small scale variation +for k=1:NUS + x=US(k).X(1:end-1); + y=US(k).Y(1:end-1); + x0=[x0,x]; + y0=[y0,y]; +% xs=x;ys=y; %1.5 mil points + if length(x)>3 + [xs,ys]=SmoothSubSampleCoastlineFast(x,y,interpDist,SmoothN); + xus=[xus,xs]; + yus=[yus,ys]; + end +end +n0=length(xus); +%Now add Pacific Territories and COFA points +%------------------------------------------------------------------------ +%NWS Pacific Region, via the Compact of Free Association, +% %oversees operations of 5 Weather Service Offices (WSO) across +% Micronesia in the Republic of Palau, Federated States of Micronesia +% and the Republic of the Marshall Islands. WFO Guam provides routine +% forecasts as well as WWA services for these areas. -Eric Lau +%Palau,Yap, Chuuk, Pohnpei, Majuro, Pago Pago, Wake island + +%Wake Island +coord=[19.2796,166.6499];%E +xus=[xus,coord(2)];yus=[yus,coord(1)]; +%Palau +coord=[7.4942, 134.5690] +xus=[xus,coord(2)];yus=[yus,coord(1)]; +%Yap:9.5557° N, 138.1399° E +coord=[9.5557, 138.1399] +xus=[xus,coord(2)];yus=[yus,coord(1)]; +%Chuuk: 7°25′N 151°47′E +coord=[7.374227, 151.754606]%E +xus=[xus,coord(2)];yus=[yus,coord(1)]; +%Pohnpei › Coordinates: 6.8519° N, 158.2147° E +coord=[6.8519, 158.2147] +xus=[xus,coord(2)];yus=[yus,coord(1)]; +%Majuro › Coordinates7.0667° N, 171.2667° E +coord=[7.0667, 171.2667] +xus=[xus,coord(2)];yus=[yus,coord(1)]; +%Pago Pago › Coordinates: 14.2732° S, 170.7030° W +coord=[-14.2732, -170.7030]% SW +xus=[xus,coord(2)];yus=[yus,coord(1)]; +% Kosrae › Coordinates :5.3096° N, 162.9815° E +coord=[5.3096, 162.9815] +xus=[xus,coord(2)];yus=[yus,coord(1)]; +%------------------------------------------------------------------------ + +%Marshall Islands +%Majuro +coord=[7.0667, 171.2667] +%xus=[xus,coord(2)];yus=[yus,coord(1)]; +%Ebeye +coord=[8.7815, 167.7373] +%xus=[xus,coord(2)];yus=[yus,coord(1)]; +%Micronesia +%Kolonia, +coord=[6.9636, 158.2102] +%xus=[xus,coord(2)];yus=[yus,coord(1)]; +%Pohnpei, +coord=[6.8519, 158.2147] +%xus=[xus,coord(2)];yus=[yus,coord(1)]; +% Chuuk-Weno +coord=[7.4523, 151.8422] +%xus=[xus,coord(2)];yus=[yus,coord(1)]; +% Tofol +coord=[5.3256, 163.0086] +%xus=[xus,coord(2)];yus=[yus,coord(1)]; +%Colonia -between Palau and Guam +coord=[9.5164,138.1222] +%xus=[xus,coord(2)];yus=[yus,coord(1)]; +n1=length(xus); +%------------------------------------------------------------------------ +%Points from Curt +%Howland Island -Baker +coord=[0.8113, -176.6183]%W +xus=[xus,coord(2)];yus=[yus,coord(1)]; +%Johnston atoll +coord=[16.7295, -169.5336]%W +xus=[xus,coord(2)];yus=[yus,coord(1)]; +%Palmyra +coord=[5.8885, -162.0787]%W +xus=[xus,coord(2)];yus=[yus,coord(1)]; +%Jarvis Island +coord=[0.3744, -159.9967]%W +xus=[xus,coord(2)];yus=[yus,coord(1)]; +%Baker Island +%0.1936° N, 176.4769° W +coord=[0.1936,-176.4769 ]%W +xus=[xus,coord(2)];yus=[yus,coord(1)]; +%------------------------------------------------------------------------ + coord=[18.4101, -75.0115 ]%W + xus=[xus,coord(2)];yus=[yus,coord(1)]; + %If your team is going to the effort to add resolution for Majuro in RMI, I would strongly suggest doing the same for these atolls in RMI: + %1. Kwajalein Atoll, which is home to part of the Ronald Regard Ballistic Missile Test Site (https://home.army.mil/kwajalein/index.php) and the underprediction of wave heights by NWS' WaveWatchIII model in January 2024, which I discussed on our last call, is what led to significant damage at the base, per: https://www.youtube.com/shorts/jH-pGoQDdcg + %2. Enewetak Atoll, which is the home of the Runit Dome (https://en.wikipedia.org/wiki/Runit_Island), is threatened by wave-driven overwash that has serious implications for the US Department of State, Department of Defense/War, and Department of the Interior via the Intergovernmental Compact of Free Association (https://www.doi.gov/oia/compacts-of-free-association). + %3. Bikini Atoll, for similar reasons as Enewetak, although the radionuclides are all over the place and not all dumped in one location. + %I would note that most of the atolls drop off at a 70-80 degree slope from approximately 30 m depth (which is generally less than 1000 m from shore) to over 1000 m depth, so there is not a need for a large region of increasing resolution to capture a broad continental shelf as characterizes CONUS. + + %Kwajalein Atoll - 9.1898° N, 167.4243° E + %Enewetak Atoll - 11.4654° N, 162.1890° E + %Bikini Atoll - 11.6065° N, 165.3768° E + + %Kwajalein Atoll - 9.1898° N, 167.4243° E + coord=[9.1898, 167.4243 ]%W + xus=[xus,coord(2)];yus=[yus,coord(1)]; + %Enewetak Atoll - 11.4654° N, 162.1890° E + coord=[ 11.4654, 162.1890]%W + xus=[xus,coord(2)];yus=[yus,coord(1)]; + %Bikini Atoll - 11.6065° N, 165.3768° E + coord=[11.6065, 165.3768 ]%W + xus=[xus,coord(2)];yus=[yus,coord(1)]; + +Blon=[129.91 10.71]; +Blat=[-30.42 79.99]; +lon=Blon;j=find(lon<90);lon(j)=lon(j)+360; +Blon=lon; +xusp=xus;j=find(xus>0);xusp(j)=xus(j)-360; + +if isplot, + clf; + plot(xusp(1:n0) ,yus(1:n0),'k.',... + xusp(n0+1:n1) ,yus(n0+1:n1),'co', ... + xusp(n1+1:end),yus(n1+1:end),'rx'); + hold on + BoundingBox(Blon-360,Blat,'r'); + grid on; + title('Updated RWPS high res target points'); + kprint('RWPSHighResPointsX.jpg'); +end +topo=BoxSmoothTopo(GlobalTopoFile,2); + +lon = topo.point.coord{:,1}; +lat = topo.point.coord{:,2}; + +%p=loadmsh('PSLGboundaryOSMxGSHHS1km.msh'); +p=loadmsh(PSLGfile); + +p.x=p.point.coord(:,1)-360; +p.y=p.point.coord(:,2); +p.edges=p.edge2.index(:,1:2); +plot(p.x,p.y,'g.'); + +%Remove near boundary points- Not land targets of resolution +dx=1; +j=find(p.xmin(p.x)+dx);p=subpslgFast(p,j); +j=find(p.ymin(p.y)+dx);p=subpslgFast(p,j); +plot(p.x,p.y,'y.'); + +j=find(xus>90); +xus(j)=xus(j)-360; + +%This loop Takes a few min:Find PSLG points near us coastline +deg2km=111.132954 +np=length(p.x); +NP=10000; + +clear d; +nus=length(xus); +for k=1:NP:np + j=k:min(np,k+NP); + x=p.x(j); + y=p.y(j); + d(j)=min(deg2km*abs([x(:)-xus(:)'].*[cos(pi*y(:)/180)*ones(1,nus)]+i*[y(:)-yus(:)'])'); + %if mod(k,1000)==0,k/np,end + k/np +end + +dmin=100; +%dmin=50; +j=find(d90); +xus(j)=xus(j)-360; +Blon=[129.91-360, 10.71]; +Blat=[-30.42, 79.99]; +if isplot, + plot(p.x,p.y,'k.'); + hold on + BoundingBox(Blon,Blat,'k'); + th=title('PSLG defining boundary'); + set(th,'FontSize',22); + kprint('PSLG.jpg') + + plot(xus,yus,'r.'); + th=title('PSLG with US coasline data points(red)'); + set(th,'FontSize',22); + kprint('PSLGwUSpoints.jpg') + + j=find(d<100); + plot(p.x(j),p.y(j),'c.'); + th=title('PSLG with US coasline data points and PSLG points near us (cyan)'); + set(th,'FontSize',22); + kprint('PSLGwUSpointsPSLGnear.jpg') +end + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%add points to off shore banks we want to refine here we have Georges Bank +%and banks around the Bahamas. + +xFB= [ -79.9909 -78.1963 -78.3957] +yFB= [23.7978 26.8928 24.1530] +xGB = -67.4517 +yGB = 41.3567 + +xx=[xUSsl(:);xus(:);xFB(:);xGB(:)]'; +yy=[yUSsl(:);yus(:);yFB(:);yGB(:)]'; +j=find(xx>90); +xx(j)=xx(j)-360; +plot(xx,yy,'c.'); + +D=DistanceToCoast(lon,lat,xx,yy); + +eval(['save -v7.3 ',FileOutMatlab, ' lon lat D xx yy xus yus xUSsl yUSsl']) ; + + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Make .msh files for jigsaw + +topo=BoxSmoothTopo(GlobalTopoFile,2); + +Dfun=topo; + +lon=Dfun.point.coord{:,1}; +j=find(lon<90);lon(j)=lon(j)+360; +j0=setdiff(1:length(lon),j);j0=j0(:); +lon=lon([j0(:);j(:)]); +D1=[D(:,j0),D(:,j)]; +Dfun.point.coord{:,1}=lon; +Dfun.value=D1; + +Dmax=20004000%max distance between two points on earth +D1(find(D1>Dmax))=Dmax; +Dfun.value=D1; + +figure;clf;pcolor(Dfun.point.coord{:,1},Dfun.point.coord{:,2},exp(-Dfun.value/320000)); +shading interp;colorbar;colormap('jet') +savemsh(DFunOutFile,Dfun); + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% save smoothed topo at same resolution as Distance function +topo=BoxSmoothTopo(GlobalTopoFile,2); + +D=topo.value; +lon=topo.point.coord{:,1}; +j=find(lon<90);lon(j)=lon(j)+360; +j0=setdiff(1:length(lon),j);j0=j0(:); +lon=lon([j0(:);j(:)]); +D1=[D(:,j0),D(:,j)]; +topo.point.coord{:,1}=lon; +topo.value=D1; + +figure;clf;pcolor(topo.point.coord{:,1},topo.point.coord{:,2},topo.value); +shading interp;colorbar;colormap('jet') +savemsh(TopoFile,topo); + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/unst_msh_gen/regional/MeshGenTemplateDirectory/MakeDistanceToCoastRWPS.m b/unst_msh_gen/regional/MeshGenTemplateDirectory/MakeDistanceToCoastRWPS.m new file mode 100644 index 0000000..662ddb5 --- /dev/null +++ b/unst_msh_gen/regional/MeshGenTemplateDirectory/MakeDistanceToCoastRWPS.m @@ -0,0 +1,219 @@ +%This script creates a "Distance to Coast file" using a US coastline file +% and custom specification of other designated points. The PSLG intended for creation of the boundary +% is used as well as a global bathymetry file and US coastline shapefile. + +SetPath +isplot=0 + +%Input files +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%PSLGfile='GlobalCoastlineGSHHS.PSLG.msh' +%PSLGfile='GlobalCoastlineOSM.PSLG.msh' +%PSLGfile='PSLGboundaryOSMxGSHHS.BOXES.msh' + +%Coastline Boundary file +%PSLGfile='GlobalCoastlineGSHHS.PSLG.msh' +%Global Bathymetry file (netcdf) +%GlobalTopoFile='/scratch3/NCEPDEV/climate/Keston.Smith/RWPS/Data/RTopo_2_0_4_GEBCO_v2023_60sec_pixel.nc' +%Shape file with US coastline +%uscl='/scratch3/NCEPDEV/climate/Keston.Smith/RWPS/Data/us_coastline/tl_2023_us_coastline.shp' + +%Output files +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%Output "distance to coast" grided jigsaw .msh file +DFunOutFile=['DFun.',PSLGfile(1:end-4),'.msh'] +%Output bathymetry on same grid as DFunOutFile +TopoFile=['Topo.',DFunOutFile]; +%Temporary matlab file +FileOutMatlab=[PSLGfile(1:end-4),'.MakeDistance.mat'] + + +US=shaperead(uscl); +NUS=length(US) +xus=[]; +yus=[]; +x0=[] +y0=[]; +interpDist=100. %meters +SmoothN=25;% box car smooth coastline at width 2(*SmoothN+1)*interpDist +% then subsample soothed coastline every SmoothN points to create smaller +% coastline data set. i.e. remove small scale variation +for k=1:NUS + x=US(k).X(1:end-1); + y=US(k).Y(1:end-1); + x0=[x0,x]; + y0=[y0,y]; +% xs=x;ys=y; %1.5 mil points + if length(x)>3 + [xs,ys]=SmoothSubSampleCoastlineFast(x,y,interpDist,SmoothN); + xus=[xus,xs]; + yus=[yus,ys]; + end +end +n0=length(xus); + +[xpi,ypi]=ExtraPointsOfIntrest + +xus=[xus(:)',xpi(:)'] +yus=[yus(:)',ypi(:)'] + +Blon=[129.91 10.71]; +Blat=[-30.42 79.99]; +Blon=LonCon(Blon) + + +if isplot, + xusp=LonCon(xus); + clf; + plot(xusp(1:n0) ,yus(1:n0),'k.',... + xusp(n0+1:n1) ,yus(n0+1:n1),'co', ... + xusp(n1+1:end),yus(n1+1:end),'rx'); + hold on + BoundingBox(Blon-360,Blat,'r'); + grid on; + title('Updated RWPS high res target points'); + kprint('RWPSHighResPointsX.jpg'); +end +topo=BoxSmoothTopo(GlobalTopoFile,2); + +lon = topo.point.coord{:,1}; +lat = topo.point.coord{:,2}; + +%p=loadmsh('PSLGboundaryOSMxGSHHS1km.msh'); +p=loadmsh(PSLGfile); + +%p.x=p.point.coord(:,1)-360; +p.x=p.point.coord(:,1); +p.y=p.point.coord(:,2); +p.x=LonCon(p.x); +p.edges=p.edge2.index(:,1:2); +plot(p.x,p.y,'g.'); + +%Remove near boundary points- Not land targets of resolution +dx=1; +j=find(p.xmin(p.x)+dx);p=subpslgFast(p,j); +j=find(p.ymin(p.y)+dx);p=subpslgFast(p,j); +plot(p.x,p.y,'y.'); + +%j=find(xus>90); +%xus(j)=xus(j)-360; + +xus=LonCon(xus); +p.x=LonCon(p.x); +%This loop Takes a few min:Find PSLG points near us coastline +deg2km=111.132954 +np=length(p.x); +NP=10000; + +t0=now; +clear d; +nus=length(xus); +for k=1:NP:np + j=k:min(np,k+NP); + x=p.x(j); + y=p.y(j); + d(j)=min(deg2km*abs([x(:)-xus(:)'].*[cos(pi*y(:)/180)*ones(1,nus)]+i*[y(:)-yus(:)'])'); + %if mod(k,1000)==0,k/np,end + if mod(k,1)==0, + t1=now; + est=(np-k)*[(t1-t0)/k]; + disp(['Computing PSLG distance to coast, etimated time remaing: ' ,num2str(est*24*60),' miniutes']); + end +end + +dmin=100; +%dmin=50; +j=find(d90); +xus(j)=xus(j)-360; +Blon=[129.91-360, 10.71]; +Blat=[-30.42, 79.99]; +if isplot, + plot(p.x,p.y,'k.'); + hold on + BoundingBox(Blon,Blat,'k'); + th=title('PSLG defining boundary'); + set(th,'FontSize',22); + kprint('PSLG.jpg') + + plot(xus,yus,'r.'); + th=title('PSLG with US coasline data points(red)'); + set(th,'FontSize',22); + kprint('PSLGwUSpoints.jpg') + + j=find(d<100); + plot(p.x(j),p.y(j),'c.'); + th=title('PSLG with US coasline data points and PSLG points near us (cyan)'); + set(th,'FontSize',22); + kprint('PSLGwUSpointsPSLGnear.jpg') +end + + + +xx=[xUSsl(:);xus(:)]'; +yy=[yUSsl(:);yus(:)]'; +xx=LonCon(xx); +plot(xx,yy,'c.'); + +save tmp.mat + +D=DistanceToCoast(lon,lat,xx,yy); + +eval(['save -v7.3 ',FileOutMatlab, ' lon lat D xx yy xus yus xUSsl yUSsl']) ; + + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Make .msh files for jigsaw + +topo=BoxSmoothTopo(GlobalTopoFile,2); + +Dfun=topo; + +lon=Dfun.point.coord{:,1}; +j=find(lon<90);lon(j)=lon(j)+360; +j0=setdiff(1:length(lon),j);j0=j0(:); +lon=lon([j0(:);j(:)]); +D1=[D(:,j0),D(:,j)]; +Dfun.point.coord{:,1}=lon; +Dfun.value=D1; + +Dmax=20004000%max distance between two points on earth +D1(find(D1>Dmax))=Dmax; +Dfun.value=D1; + +figure;clf;pcolor(Dfun.point.coord{:,1},Dfun.point.coord{:,2},exp(-Dfun.value/320000)); +shading interp;colorbar;colormap('jet') +savemsh(DFunOutFile,Dfun); + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% save smoothed topo at same resolution as Distance function +topo=BoxSmoothTopo(GlobalTopoFile,2); + +D=topo.value; +lon=topo.point.coord{:,1}; +j=find(lon<90);lon(j)=lon(j)+360; +j0=setdiff(1:length(lon),j);j0=j0(:); +lon=lon([j0(:);j(:)]); +D1=[D(:,j0),D(:,j)]; +topo.point.coord{:,1}=lon; +topo.value=D1; + +figure;clf;pcolor(topo.point.coord{:,1},topo.point.coord{:,2},topo.value); +shading interp;colorbar;colormap('jet') +savemsh(TopoFile,topo); + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/unst_msh_gen/regional/MeshGenTemplateDirectory/MakeRWPSMesh.m b/unst_msh_gen/regional/MeshGenTemplateDirectory/MakeRWPSMesh.m new file mode 100644 index 0000000..96c60b7 --- /dev/null +++ b/unst_msh_gen/regional/MeshGenTemplateDirectory/MakeRWPSMesh.m @@ -0,0 +1,10 @@ + +SetPath +lonWest=129.91;lonEast=10.71;latSouth=-30.42;latNorth=79.99; +CoastLineFile = 'GlobalCoastlineGSHHS.shp' +BuildBoundaryPSLGfunction(CoastLineFile,lonWest,lonEast,latSouth,latNorth) +[xpi,ypi]=ExtraPointsOfIntrest; +TargetShap=uscl; +%DX=.1; +%MakeDistanceToCoastData(DX,TargetShape,xpi,ypi) +MakeDistanceToCoastRWPS diff --git a/unst_msh_gen/regional/MeshGenTemplateDirectory/MergePslgsOSMxGSSHS.m b/unst_msh_gen/regional/MeshGenTemplateDirectory/MergePslgsOSMxGSSHS.m new file mode 100644 index 0000000..cd787cb --- /dev/null +++ b/unst_msh_gen/regional/MeshGenTemplateDirectory/MergePslgsOSMxGSSHS.m @@ -0,0 +1,67 @@ + + +OSMmsh='./PSLGboundary1kmP_NewOrleans.OSM.msh' +GSHHGmsh='./PSLGboundary1kmP_NewOrleansB.msh' +p0=loadmsh(OSMmsh) +p1=loadmsh(GSHHGmsh) + +p0.x=p0.point.coord(:,1); +p0.y=p0.point.coord(:,2); +p0.edges=p0.edge2.index(:,1:2); + + +p1.x=p1.point.coord(:,1); +p1.y=p1.point.coord(:,2); +p1.edges=p1.edge2.index(:,1:2); + +close all +clf; +plot(p0.x,p0.y,'b.',p1.x,p1.y,'r.'); +axis equal; +hold on +axAS = [183.0293 191.6089 -24.5496 -11.9295] +BoundingBox(axAS(1:2),axAS(3:4),'c') + +axPalau =[ 130.8368 141.8230 3.9914 10.1256] +BoundingBox(axPalau(1:2),axPalau(3:4),'c') + +axSP =[ 211.0219 220.1817 -18.9565 -13.8421] +BoundingBox(axSP(1:2),axSP(3:4),'c') + +axCP =[ 151.1962 152.3336 6.9451 7.5801] +BoundingBox(axCP(1:2),axCP(3:4),'c') + +axGOM =[ 290.8507 291.1934 43.7119 43.9032] +BoundingBox(axGOM(1:2),axGOM(3:4),'c') + +axCar =[ 272.0041 272.7733 17.1417 17.5712] +BoundingBox(axCar(1:2),axCar(3:4),'c'); + +axCarB =[ 272.9356 274.2177 16.0475 16.7634]; +BoundingBox(axCarB(1:2),axCarB(3:4),'c'); + +AX=[axAS;axPalau;axSP;axCP;axGOM;axCar;axCarB] + +[nax,four]=size(AX); +p=p1;%gshhs baseline +for k=1:nax + j0=FindPointsAx(AX(k,:),p0.x,p0.y); + j=FindPointsAx(AX(k,:),p.x,p.y); + p0a=subpslgFast(p0,j0); + nn=length(p.x); + k=setdiff(1:nn,j); + p=subpslgFast(p,k);%remove gshhs features in box + p0a=subpslgFast(p0,j0);%get OSM features in box + p=joinpslg(p,p0a);%add OSM features to remaining gshhs +end + +pslg=p; +save pslgOSMxGSHHS1kmBOXES.mat pslg + +geom=pslg2geom(pslg) + +savemsh('PSLGboundaryOSMxGSHHS1kmBOXES.msh',geom) + +hold on; +plot(p.x,p.y,'k.'); +title('red-gshhs, blue- OSM, black- final') diff --git a/unst_msh_gen/regional/MeshGenTemplateDirectory/MergePslgsOSMxGSSHS_14N230W.m b/unst_msh_gen/regional/MeshGenTemplateDirectory/MergePslgsOSMxGSSHS_14N230W.m new file mode 100644 index 0000000..e5067d4 --- /dev/null +++ b/unst_msh_gen/regional/MeshGenTemplateDirectory/MergePslgsOSMxGSSHS_14N230W.m @@ -0,0 +1,77 @@ + + +OSMmsh='../RWPSmeshtrial.OSM/PSLGboundary1kmP_NewOrleans.msh' +GSHHGmsh='../RWPSmeshtrial.PIXX.AP1km/PSLGboundary1kmP_NewOrleans.msh' +p0=loadmsh(OSMmsh) +p1=loadmsh(GSHHGmsh) + +p0.x=p0.point.coord(:,1); +p0.y=p0.point.coord(:,2); +p0.edges=p0.edge2.index(:,1:2); + + +p1.x=p1.point.coord(:,1); +p1.y=p1.point.coord(:,2); +p1.edges=p1.edge2.index(:,1:2); + +close all +plot(p0.x,p0.y,'b.',p1.x,p1.y,'r.') + +yc=14.5 +xc=230 +jx0=find(p0.xyc ) +nb=find(p.y(j)xc ) +nb=find(p.x(j) xmin , topo.xgrid < xmax ) +ymsk = np.logical_and( topo.ygrid > ymin , topo.ygrid < ymax ) + +tv = tv[:, xmsk] +tv = tv[ymsk, :] +dv = dv[:, xmsk] +dv = dv[ymsk, :] + +# define spatial resolution shape function----------------------------- +W=np.exp(- (np.abs(dv-d0) / d1) - (np.abs(tv) / beta) ) +W[ np.where(dv < d0 ) ] = 1. + +# build resolution specification gridded data structure, hmat---------- +hmat=topo +hmat.value=Smax - (Smax - Smin)*W + +hmat.mshID = "ellipsoid-grid" +hmat.radii = np.full( +3, +6371.0, dtype=jigsawpy.jigsaw_msh_t.REALS_t) + +hmat.xgrid = hmat.xgrid[xmsk] * np.pi / 180. +hmat.ygrid = hmat.ygrid[ymsk] * np.pi / 180. + +hmat.value = np.maximum(hmat.value, Smin)#should not be nescesary +hmat.value = np.minimum(hmat.value, Smax) + +hmat.slope = np.full( hmat.value.shape, +0.1500 , dtype=jigsawpy.jigsaw_msh_t.REALS_t) + +#------------------------------------ do stereographic proj. +geom.point["coord"][:, :] *= np.pi / 180. + +proj.prjID = 'stereographic' +proj.radii = +6.371E+003 +proj.xbase = +0.500 * (xmin + xmax) * np.pi / 180. +proj.ybase = +0.500 * (ymin + ymax) * np.pi / 180. + +jigsawpy.savemsh(OutDir+"HFUN.msh", hmat) + +jigsawpy.project(geom, proj, "fwd") +jigsawpy.project(hmat, proj, "fwd") + +jigsawpy.savemsh(opts.geom_file, geom) +jigsawpy.savemsh(opts.hfun_file, hmat) + +# save hmat------------------------------------------------------------ +jigsawpy.savemsh(OutDir+"HFUNproj0.msh", hmat) + +#smooth hmat +jigsawpy.cmd.marche(opts, hmat) + +# save smoothed hmat--------------------------------------------------- +jigsawpy.savemsh(OutDir+"HFUNproj1.msh", hmat) + +# make mesh using JIGSAW----------------------------------------------- +opts.hfun_scal = "absolute" +opts.hfun_hmax = float("inf") # null HFUN limits +opts.hfun_hmin = float(+0.00) +#opts.hfun_hmax = 1.25*Smax # Unintended effects, better off null +#opts.hfun_hmin = .5*Smin + +opts.mesh_dims = +2 # 2-dim. simplexes +opts.mesh_eps1 = +1. + +#opts.mesh_top1 = "true" !!!No convergece + + +ttic = time.time() + +jigsawpy.cmd.jigsaw(opts, mesh) + +ttoc = time.time() + +print("CPUSEC =", (ttoc - ttic)) + +# save mesh in JIGSAW native projection based on input PSLG------------ +jigsawpy.savemsh(OutDir+"/RWPS.PROJ.msh",mesh) + +# compute costa functions and save +cost = jigsawpy.triscr2(mesh.point["coord"],mesh.tria3["index"]) +np.savetxt(OutDir+"TriScr2.txt",cost,"%f") +print("TRISCR =", np.min(cost), np.mean(cost)) + +cost = jigsawpy.pwrscr2(mesh.point["coord"], mesh.power, mesh.tria3["index"]) +np.savetxt(OutDir+"PwrScr2.txt",cost,"%f") +print("PWRSCR =", np.min(cost), np.mean(cost)) + +tbad = jigsawpy.centre2(mesh.point["coord"],mesh.power,mesh.tria3["index"]) +print("OBTUSE =",+np.count_nonzero(np.logical_not(tbad))) + + +# project mesh nodes to radian lat, lon +jigsawpy.project(mesh, proj, "inv") # This used to work +#jigsawpy.savemsh("RWPS.radian.msh",mesh) + +# transform mesh nodes to degree lat, lon +mesh.point["coord"][:, :] = mesh.point["coord"][:, :]*180. / np.pi + +# save mesh in degree lat, lon system +jigsawpy.savemsh(OutDir+"RWPS.LL.msh",mesh) + +# create jigsaw R3 mesh on global surface and save to +S2=mesh.point["coord"][:,[0,1]] +S2=S2*np.pi/180. + +R3=jigsawpy.S2toR3(mesh.radii,S2) +#np.savetxt("R3.txt",R3," %f ") # save 3D nodes + +meshR3 = jigsawpy.jigsaw_msh_t() +mesh.mshID = 'ellipsoid-mesh' +meshR3.tria3=mesh.tria3 +meshR3.ndims=3 +#make 3D coordinates +nd=R3.shape +meshR3.vert3 = np.zeros(nd[0], dtype=mesh.VERT3_t) +meshR3.vert3["coord"] = R3 +jigsawpy.savemsh(OutDir+"RWPS.R3.msh",meshR3) + + +# Now apply filters and output in WW3 form +# import FilterRoutines.py + +from FilterRoutinesNM import * + +#replace mesh with R3 mesh, mesh->mesh R3 +jigsawpy.loadmsh(OutDir+"RWPS.R3.msh", mesh) # uncomment if starting here + +opts.geom_file = "geom.msh" #saves the geometry info for jigsaw +opts.jcfg_file = "opts.jig" #jigsaw ctlr file + + +geom.mshID = "ellipsoid-mesh" +geom.radii = np.full(3, 6.371E+003, dtype=geom.REALS_t) + +jigsawpy.savemsh(opts.geom_file, geom) + +inject_dem() +filter_ocn() + +jigsawpy.savemsh(OutDir+"RWPS.F.R3.msh", mesh) + +# viz. in eg. paraview +jigsawpy.savevtk(OutDir+"test.vtk", mesh) + +# convert to lon lat +point = mesh.point["coord"] +point = jigsawpy.R3toS2(geom.radii, point) +point*= 180. / np.pi + +depth = np.reshape(-1*mesh.value, (mesh.value.size, 1)) +depth[depth <= 0] = 2 +point = np.hstack((point, depth)) # append elev. as 3rd coord. +cells = [("triangle", mesh.tria3["index"])] +tri_data=cells[0][1]+1 + +#put coordinates in non standard format to avoid international date line +lon=point[:,0] +lon[np.where(lon>90)]=lon[np.where(lon>90)]-360 +point[:,0]=lon + +write_gmsh_mesh(OutDir+"RWPS.ww3", point, tri_data) + +mesh.point["coord"]=point +jigsawpy.savemsh(OutDir+"RWPS.F.LLH.msh", mesh) + + +#write final mesh in jigsaw .msh format +meshR2 = jigsawpy.jigsaw_msh_t() +#make 2D coordinates +nd=point.shape +meshR2.ndims=2 +meshR2.vert2 = np.zeros(nd[0], dtype=mesh.VERT2_t) +meshR2.vert2["coord"] = point[:,[0,1]] +meshR2.tria3=mesh.tria3 +meshR2.mshID=mesh.mshID + +jigsawpy.savemsh(OutDir+"RWPS.F.LL.msh", meshR2) + diff --git a/unst_msh_gen/regional/MeshGenTemplateDirectory/PostProcessGrid.m b/unst_msh_gen/regional/MeshGenTemplateDirectory/PostProcessGrid.m new file mode 100644 index 0000000..cf28f27 --- /dev/null +++ b/unst_msh_gen/regional/MeshGenTemplateDirectory/PostProcessGrid.m @@ -0,0 +1,395 @@ + + +SetPath + +%script to handle all post jigsaw mesh editing. +%Post processing steps are as follows +% (A) Remove sand points +% (B) Merge inland lakes --> 'RWPS.WW3a.lakes.msh' +% (C) Remove islands that jigsaw has meshed over +% (D) Remove sand points that can be created (rarely) with island removal --> 'RWPS.WW3a1.lakes.msh' +% INACTIVE (E) Examine Pacific island resolution and make changes to island editing if needed --> 'RWPS.WW3b.lakes.msh' +% INACTIVE (F) Examine mesh near New Orleans where boundary has been merged to reflect new marine zones --> 'RWPS.WW3c.lakes.msh' +% INACTIVE (G) Remove sand points that can be created (rarely) with New Orleans editing --> 'RWPS.WW3d.lakes.msh' +% (H) Stretch open ocean boundary nodes back to origonal specified boundary rectangle --> 'RWPS.WW3e.lakes.msh' +% this is an artificat of the projection used in jigsaw +% (I) Remove sand points that can be created (Should not matter) with boundary node stretching --> 'RWPS.WW3f.lakes.msh' +% (J) Write final WW3 mesh --> 'RWPS.WW3g.lakes.msh' +% this is the mesh to run WW3 on. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%Input file from jigsaw and boundary file used in it's creation: + +SetPath +isplot=0; +outdir='RWPS.1/' +%pslgfile='GlobalCoastlineGSHHS.PSLG.msh' +pslgfile=PSLGfile, %global variable filename set in SetPath + +jigsawout='RWPS.F.LLH' + +WW3FileOut='RWPS.GSHHS.WW3.msh' + +%%%%%%%%%%%%%%%%%%%%%%%%%%%% STEP (A) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +g=loadmsh([outdir,jigsawout,'.msh']); + +%remove sand points on boundary + +RemoveSandPoints([outdir,jigsawout,'.msh'],pslgfile,[outdir,jigsawout,'.NSP.msh'],[outdir,jigsawout,'.NSP.WW3.msh']); + +%%%%%%%%%%%%%%%%%%%%%%%%%%%% STEP (B) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%Add Lakes to Mesh +%g=loadmshWW3('RWPSMeshOSMxGSHHS.BoxesFiles/RWPS.F.LLH.NSP.WW3.msh'); +g=loadmshWW3( [outdir,jigsawout,'.NSP.WW3.msh']); +g.x=LonCon(g.x); + +%LakeDir='/mnt/sda/keston/RWPSLakes/' +LakeDir='/scratch3/NCEPDEV/climate/Keston.Smith/RWPS/RWPSLakes/' + +gS=loadmshWW3([LakeDir,'Sebago.NWPS.WW3.msh']); +gS.x=LonCon(gS.x); +gW=loadmshWW3([LakeDir,'Winnipesaukee.NWPS.WW3.msh']) +gW.x=LonCon(gW.x); +gO=loadmshWW3([LakeDir,'Okeechobee.NWPS.WW3.msh']) +gO.x=LonCon(gO.x); + +g=CombineMesh(g,gO); +g=CombineMesh(g,gS); +g=CombineMesh(g,gW); + +x=g.x;y=g.y;z=g.z;e=g.e; + +if isplot, + LS=ComputeLengthScale_wgs84_MEL(x,y,e);LSn=Ele2Nodes(x,y,e,LS); + clf;ph=patch(x(e'),y(e'),LSn(e'));cm=colormap('jet');shading interp;axis equal; + caxis([0,12]) + colormap(flip(cm)); +end + +WriteWW3MeshX(g,'RWPS.WW3a.lakes.msh'); + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%% STEP (C) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%clear +g=loadmshWW3('RWPS.WW3a.lakes.msh') +%Remove key islands remaining in mesh that are present in the PSLG file + +p=loadmsh(pslgfile); + +p.x=p.point.coord(:,1); +p.y=p.point.coord(:,2); +p.edges=p.edge2.index(:,1:2); +%p.x=p.x-360; +p.x=LonCon(p.x); +x=g.x;y=g.y;z=g.z;e=g.e; + +if isplot, + clf;ph=patch(x(e'),y(e'),z(e'));cm=colormap('jet');shading interp;axis equal; + hold on + plot(p.x,p.y,'k.') +end + +MinBndDist=1000; +gnew=g; + +%go through all longitude and remove nodes +dx=2; +dxb=10 +%close all +RemoveIsl=1; +n=1 +if RemoveIsl, + for xx=(ceil(min(g.x))+dxb):dx:(floor(max(g.x))-dxb) + ax11=[xx-dx,xx+dx,min(g.y)+dxb,max(g.y)-dxb] + N(n)=length(gnew.x); + AX(n,:)=ax11; + gnew=RemoveMissingIslandsEle(gnew,ax11,p,MinBndDist,0); + n=n+1; + n/243 + length(gnew.x) + %figure(3);clf;plot(N,'ko-');pause(.001) + end +% figure(3);clf;plot(AX(:,1),N,'ko-'); +end + + +g=gnew + +%%%%%%%%%%%%%%%%%%%%%%%%%%%% STEP (D) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +g=RemoveSandPointsWW3(g,'RWPS.WW3a1.lakes.msh') + +%%%%%%%%%%%%%%%%%%%%%%%%%%%% STEP (E) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +p=loadmsh(pslgfile); +p.x=p.point.coord(:,1);p.y=p.point.coord(:,2);p.edges=p.edge2.index(:,1:2); +p.x=LonCon(p.x); +x=g.x;y=g.y;z=g.z;e=g.e; +if isplot, + LS=ComputeLengthScale_wgs84_MEL(x,y,e);LSn=Ele2Nodes(x,y,e,LS); + clf;ph=patch(x(e'),y(e'),LSn(e'));cm=colormap('jet');shading interp;axis equal; + caxis([0,12]) + colormap(flip(cm)); + hold on; + plot(p.x,p.y,'k.'); +end + +p=loadmsh(pslgfile); +%p.x=p.point.coord(:,1);p.x=p.x-360;p.y=p.point.coord(:,2);p.edges=p.edge2.index(:,1:2); +p.x=p.point.coord(:,1);p.y=p.point.coord(:,2);p.edges=p.edge2.index(:,1:2); +p.x=LonCon(p.x); + +g0=g; +gnew=g +if isplot + axAS =[ -179.6738 -159.5513 -18.4086 -7.1731] + axis(axAS);pause(5) + + axTofol =[ -197.5575 -196.5479 4.8724 5.7902] + axis(axTofol);pause(5) + + axPA = [-177.6646 -175.5927 -0.2906 1.3435] + axis(axPA);pause(5) + + axJA =[ -172.1141 -165.7656 14.1706 19.1777] + axis(axJA);pause(5) + + axMWI =[ -179.5965 -176.0019 27.0046 29.8397] + axis(axMWI);pause(5) + + axTin=[-215.1401 -213.4623 14.3922 15.7155] + axis(axTin);pause(5) + + axNNMI =[ -215.9834 -213.8052 19.3525 21.0704]; + axis(axNNMI);pause(5) + + axNNMI1=[-214.8924 -213.6875 14.6480 15.5983]; + axis(axNNMI1);pause(5) + + axMaj =[ -177.2629 -175.7651 -0.1340 1.0474] + axis(axMaj);pause(5) + + axHI =[ -179.1073 -153.7595 11.4935 31.4855] + axis(axHI);pause(5) + + axChuuk =[ -198.1190 -196.0417 4.7967 5.8354] + axis(axChuuk);pause(5) +end + +WriteWW3MeshX(gnew,'RWPS.WW3b.lakes.msh') + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%% STEP (F) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%fix new orleans +%clear +g=loadmshWW3('RWPS.WW3b.lakes.msh') +%Examine and Hand Edit Mesh around new orleans to relect changes if nescesary +%Should not be nescesary with +if isplot, + x=g.x;y=g.y;z=g.z;e=g.e; + LS=ComputeLengthScale_wgs84_MEL(x,y,e); + LSn=Ele2Nodes(x,y,e,LS); + clf;ph=patch(x(e'),y(e'),LSn(e'));cm=colormap('jet');shading interp;axis equal; + colormap(flip(cm)); + caxis([0,12]); + % Shape file of new marine zones in New Orleans + S=shaperead('/scratch3/NCEPDEV/climate/Keston.Smith/RWPS/Data/us_coastline/mz03mr26_LIX.shp') + hold on + for k=1:length(S),plot(S(k).X,S(k).Y,'k');end + + axNOZ =[ -91.6701 -87.7499 28.7284 30.9173] + axis(axNOZ) + gnew=g; + eno=input('Enter 1 if you want to edit the mesh around New Orleans:' ) + if eno, + gnew=RemoveMeshParts(g,axNOZ,S,[0,50]); + else + gnew=g; + end +else + gnew=g;% no edit +end + +WriteWW3MeshX(gnew,'RWPS.WW3c.lakes.msh') + +%confirm no introduction of sand points +%%%%%%%%%%%%%%%%%%%%%%%%%%%% STEP (G) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +g=RemoveSandPointsWW3(gnew,'RWPS.WW3d.lakes.msh') + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%% STEP (H) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%Fix boundary warping from projection in mesh generation +%clear +close all +g=loadmshWW3('RWPS.WW3d.lakes.msh') +g0=g; + +Blon=[129.91 10.71] +Blat=[-30.42 79.99] +%Blon(1)=Blon(1)-360 +Blon=LonCon(Blon); +x=g.x;y=g.y;z=g.z;e=g.e; +jb=g.bnd; +bnd0=detbndy(g.e) + +if isplot, + clf;ph=patch(x(e'),y(e'),z(e')); + cm=colormap('jet');shading interp;hold on + colormap(flip(cm)); + axis equal;hold on + plot(g.x(jb),g.y(jb),'k.');hold on +end +js=find(g.y(jb)max(Blat)); +g.y(jb(js))=max(Blat); +if isplot,plot(g.x(jb(js)),g.y(jb(js)),'r.');end + +%dx=degree latitude delta to discriminate land points near boundary from open ocean boundary +dx=.05; +js=find(and( g.x(jb)>min(Blon),g.x(jb)max(Blon)-dx )); +g.x(jb(js))=max(Blon); +if isplot,plot(g.x(jb(js)),g.y(jb(js)),'r.');end +WriteWW3MeshX(g,'RWPS.WW3e.lakes.msh') + +%confirm no introduction of sand points +%%%%%%%%%%%%%%%%%%%%%%%%%%%% STEP (I) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +g0=RemoveSandPointsWW3(g,'RWPS.WW3f.lakes.msh') + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%% STEP (J) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%WriteWW3MeshX(g0,'RWPS.WW3g.lakes.msh') +WriteWW3MeshX(g0,WW3FileOut); +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%Some Plotting +if isplot, + g=g0; + + + %clear + g=loadmshWW3('RWPS.WW3g.lakes.msh')%RWPS.PIXAllLnwps.PP.WW3c.msh'); + x=g.x;y=g.y;z=g.z;e=g.e; + LS=ComputeLengthScale_wgs84_MEL(x,y,e);LSn=Ele2Nodes(x,y,e,LS); + clf;ph=patch(x(e'),y(e'),LSn(e'));cm=colormap('jet');shading interp;axis equal; + jb=g.bnd; + hold on; + plot(g.x(jb),g.y(jb),'k.');hold on + + + f=z; + clf;patch(x(e'),y(e'),f(e'));cm=colormap('jet');shading interp;axis equal; + colormap(flip(cm)); + caxis([0,6000]); + kprint('Bathy.jpg'); + + clf; + caxis([0,6000]); + colormap(flip(cm)); + + hcb=colorbar('h','position',[.1,.075,.8,.025]) + %hcb.Label.String='(km)' + set(gca,'visible','off') + kprint('BathyColorbarH.jpg'); + + clf; + colorbar; + colormap(flip(cm)); + caxis([0,6000]); + kprint('BathyColorbarV.jpg'); + + + LS=ComputeLengthScale_wgs84_MEL(x,y,e); + LSn=Ele2Nodes(x,y,e,LS); + + clf;patch(x(e'),y(e'),LSn(e'));cm=colormap('jet');shading interp;axis equal; + colormap(flip(cm)); + caxis([0,12]); + kprint('Lengthscale.jpg'); + + + axCAR =[ -85.4065 -61.7227 8.8200 26.8661] + ax=axCAR + axis(ax);daspect([1,cos(pi*ax(3)/180),1]) + kprint('Caribean.jpg') + + + axAK =[ -192.1450 -152.2804 47.2479 77.6231] + ax=axAK + axis(ax);daspect([1,cos(pi*ax(3)/180),1]) + kprint('Alaska.jpg') + + axHI =[ -179.5950 -152.9066 13.8641 34.1996] + ax=axHI + axis(ax);daspect([1,cos(pi*ax(3)/180),1]) + kprint('Hawaii.jpg') + + + axMI =[ -220.2161 -208.6314 12.5003 21.3274]; + ax=axMI + axis(ax);daspect([1,cos(pi*ax(3)/180),1]) + kprint('MarianaIslands.jpg') + + axAS =[ -173.3623 -168.3684 -16.4846 -12.0090] + ax=axAS + axis(ax);daspect([1,cos(pi*ax(3)/180),1]) + kprint('AmericanSamoa.jpg') + + + axMicro =[ -210.3818 -185.8701 -3.7494 15.5832] + ax=axMicro + axis(ax);daspect([1,cos(pi*ax(3)/180),1]) + kprint('Micronesia.jpg') + + axWMicro =[ -226.6896 -214.3594 5.3753 15.1002] + ax=axWMicro + axis(ax);daspect([1,cos(pi*ax(3)/180),1]) + kprint('WestMicronesia.jpg') + + + figure; + clf; + caxis([0,12]); + colormap(flip(cm)); + + hcb=colorbar('h','position',[.1,.075,.8,.025]) + hcb.Label.String='(km)' + set(gca,'visible','off') + kprint('LengthScaleColorbarH.jpg'); + + clf + caxis([0,12]); + colormap(flip(cm)); + + % hcb=colorbar('v','position',[.1,.075,.8,.025]) + vcb=colorbar('v','position',[.075,.1,.025,.8]) + vcb.Label.String='(km)' + set(gca,'visible','off') + kprint('LengthScaleColorbarV.jpg'); + + + clf + caxis([0,6000]); + colormap(flip(cm)); + + hcb=colorbar('h','position',[.1,.075,.8,.025]) + hcb.Label.String='(m)' + set(gca,'visible','off') + kprint('BathyColorbarH.jpg'); + + clf + caxis([0,6000]); + colormap(flip(cm)); + + % hcb=colorbar('v','position',[.1,.075,.8,.025]) + vcb=colorbar('v','position',[.075,.1,.025,.8]) + vcb.Label.String='(m)' + set(gca,'visible','off') + kprint('BathyColorbarV.jpg'); + +end diff --git a/unst_msh_gen/regional/MeshGenTemplateDirectory/PostProcessGrid_NWcoastal.m b/unst_msh_gen/regional/MeshGenTemplateDirectory/PostProcessGrid_NWcoastal.m new file mode 100644 index 0000000..26c8472 --- /dev/null +++ b/unst_msh_gen/regional/MeshGenTemplateDirectory/PostProcessGrid_NWcoastal.m @@ -0,0 +1,145 @@ + + +SetPath + +%script to handle all post jigsaw mesh editing. +%Post processing steps are as follows +% (A) Remove sand points +% (B) Merge inland lakes --> 'RWPS.WW3a.lakes.msh' +% (C) Remove islands that jigsaw has meshed over +% (D) Remove sand points that can be created (rarely) with island removal --> 'RWPS.WW3a1.lakes.msh' +% INACTIVE (E) Examine Pacific island resolution and make changes to island editing if needed --> 'RWPS.WW3b.lakes.msh' +% INACTIVE (F) Examine mesh near New Orleans where boundary has been merged to reflect new marine zones --> 'RWPS.WW3c.lakes.msh' +% INACTIVE (G) Remove sand points that can be created (rarely) with New Orleans editing --> 'RWPS.WW3d.lakes.msh' +% (H) Stretch open ocean boundary nodes back to origonal specified boundary rectangle --> 'RWPS.WW3e.lakes.msh' +% this is an artificat of the projection used in jigsaw +% (I) Remove sand points that can be created (Should not matter) with boundary node stretching --> 'RWPS.WW3f.lakes.msh' +% (J) Write final WW3 mesh --> 'RWPS.WW3g.lakes.msh' +% this is the mesh to run WW3 on. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%Input file from jigsaw and boundary file used in it's creation: + +SetPath +isplot=0; +outdir='NWcoastal/' +pslgfile=PSLGfile, %global variable filename set in SetPath + +jigsawout='RWPS.F.LLH' + +WW3FileOut='NWcoastal.WW3.msh' + +%%%%%%%%%%%%%%%%%%%%%%%%%%%% STEP (A) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +g=loadmsh([outdir,jigsawout,'.msh']); + +%remove sand points on boundary + +RemoveSandPoints([outdir,jigsawout,'.msh'],pslgfile,[outdir,jigsawout,'.NSP.msh'],[outdir,jigsawout,'.NSP.WW3.msh']); + +%%%%%%%%%%%%%%%%%%%%%%%%%%%% STEP (B) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +g=loadmshWW3( [outdir,jigsawout,'.NSP.WW3.msh']); +g.x=LonCon(g.x); + +if isplot, + LS=ComputeLengthScale_wgs84_MEL(x,y,e);LSn=Ele2Nodes(x,y,e,LS); + clf;ph=patch(x(e'),y(e'),LSn(e'));cm=colormap('jet');shading interp;axis equal; + caxis([0,12]) + colormap(flip(cm)); +end + +WriteWW3MeshX(g,'RWPS.WW3a.msh'); + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%% STEP (C) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%clear +g=loadmshWW3('RWPS.WW3a.msh') +%Remove key islands remaining in mesh that are present in the PSLG file + +p=loadmsh(pslgfile); + +p.x=p.point.coord(:,1); +p.y=p.point.coord(:,2); +p.edges=p.edge2.index(:,1:2); +p.x=LonCon(p.x); +x=g.x;y=g.y;z=g.z;e=g.e; + +if isplot, + clf;ph=patch(x(e'),y(e'),z(e'));cm=colormap('jet');shading interp;axis equal; + hold on + plot(p.x,p.y,'k.') +end + +MinBndDist=1000; +gnew=g; + +%go through all longitude and remove nodes +dx=2; +dxb=10 +%close all +RemoveIsl=1; +n=1 +if RemoveIsl, + for xx=(ceil(min(g.x))+dxb):dx:(floor(max(g.x))-dxb) + ax11=[xx-dx,xx+dx,min(g.y)+dxb,max(g.y)-dxb] + N(n)=length(gnew.x); + AX(n,:)=ax11; + gnew=RemoveMissingIslandsEle(gnew,ax11,p,MinBndDist,0); + n=n+1; + length(gnew.x) + %figure(3);clf;plot(N,'ko-');pause(.001) + end +end + +g=gnew + +%%%%%%%%%%%%%%%%%%%%%%%%%%%% STEP (D) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +g=RemoveSandPointsWW3(g,'RWPS.WW3a1.msh') + +%%%%%%%%%%%%%%%%%%%%%%%%%%%% STEP (H) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%Fix boundary warping from projection in mesh generation +%clear +close all +g=loadmshWW3('RWPS.WW3a1.msh') +g0=g; + +Blon=[round(min(g.x)),round(max(g.x))] +Blat=[round(min(g.y)),round(max(g.y))] +Blon=LonCon(Blon); + +x=g.x;y=g.y;z=g.z;e=g.e; +jb=g.bnd; +bnd0=detbndy(g.e) + +if isplot, + clf;ph=patch(x(e'),y(e'),z(e')); + cm=colormap('jet');shading interp;hold on + colormap(flip(cm)); + axis equal;hold on + plot(g.x(jb),g.y(jb),'k.');hold on +end +js=find(g.y(jb)max(Blat)); +g.y(jb(js))=max(Blat); +if isplot,plot(g.x(jb(js)),g.y(jb(js)),'r.');end + +%dx=degree latitude delta to discriminate land points near boundary from open ocean boundary +dx=.05; +js=find(and( g.x(jb)>min(Blon),g.x(jb)max(Blon)-dx )); +g.x(jb(js))=max(Blon); +if isplot,plot(g.x(jb(js)),g.y(jb(js)),'r.');end +WriteWW3MeshX(g,'RWPS.WW3b.msh') + +%confirm no introduction of sand points +%%%%%%%%%%%%%%%%%%%%%%%%%%%% STEP (I) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +g0=RemoveSandPointsWW3(g,'RWPS.WW3c.msh') + +%%%%%%%%%%%%%%%%%%%%%%%%%%%% STEP (J) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +WriteWW3MeshX(g0,WW3FileOut); +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/unst_msh_gen/regional/MeshGenTemplateDirectory/PostProcessGrid_OSMxGSHHS_NewOrl.m b/unst_msh_gen/regional/MeshGenTemplateDirectory/PostProcessGrid_OSMxGSHHS_NewOrl.m new file mode 100644 index 0000000..39ec8a0 --- /dev/null +++ b/unst_msh_gen/regional/MeshGenTemplateDirectory/PostProcessGrid_OSMxGSHHS_NewOrl.m @@ -0,0 +1,382 @@ + +addpath ../matlab +addpath /scratch3/NCEPDEV/climate/Keston.Smith/MeshGenMatlabLibs/jigsaw-matlab +addpath /scratch3/NCEPDEV/climate/Keston.Smith/MeshGenMatlabLibs/InsidePoly + +%script to handle all post jigsaw mesh editing. +%Post processing steps are as follows +% (A) Remove sand points +% (B) Merge inland lakes --> 'RWPS.WW3a.lakes.msh' +% (C) Remove islands that jigsaw has meshed over +% (D) Remove sand points that can be created (rarely) with island removal --> 'RWPS.WW3a1.lakes.msh' +% INACTIVE (E) Examine Pacific island resolution and make changes to island editing if needed --> 'RWPS.WW3b.lakes.msh' +% INACTIVE (F) Examine mesh near New Orleans where boundary has been merged to reflect new marine zones --> 'RWPS.WW3c.lakes.msh' +% INACTIVE (G) Remove sand points that can be created (rarely) with New Orleans editing --> 'RWPS.WW3d.lakes.msh' +% (H) Stretch open ocean boundary nodes back to origonal specified boundary rectangle --> 'RWPS.WW3e.lakes.msh' +% this is an artificat of the projection used in jigsaw +% (I) Remove sand points that can be created (Should not matter) with boundary node stretching --> 'RWPS.WW3f.lakes.msh' +% (J) Write final WW3 mesh --> 'RWPS.WW3g.lakes.msh' +% this is the mesh to run WW3 on. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%Input file from jigsaw and boundary file used in it's creation: +isplot=0; +outdir='RWPS.OSMxGSHHS.NewOrl/' +pslgfile='/scratch3/NCEPDEV/climate/Keston.Smith/RWPS/Data/JigsawFormatFiles/PSLGboundaryOSMxGSHHS1kmBOXES.msh' +jigsawout='RWPS.F.LLH' + +%%%%%%%%%%%%%%%%%%%%%%%%%%%% STEP (A) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +g=loadmsh([outdir,jigsawout,'.msh']); + +%remove sand points on boundary + +RemoveSandPoints([outdir,jigsawout,'.msh'],pslgfile,[outdir,jigsawout,'.NSP.msh'],[outdir,jigsawout,'.NSP.WW3.msh']); + +%%%%%%%%%%%%%%%%%%%%%%%%%%%% STEP (B) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%Add Lakes to Mesh +%g=loadmshWW3('RWPSMeshOSMxGSHHS.BoxesFiles/RWPS.F.LLH.NSP.WW3.msh'); +g=loadmshWW3( [outdir,jigsawout,'.NSP.WW3.msh']); + +gS=loadmshWW3('/scratch3/NCEPDEV/climate/Keston.Smith/RWPS/RWPSLakes/Sebago.NWPS.WW3.msh'); +gS.x=gS.x-360; +gW=loadmshWW3('/scratch3/NCEPDEV/climate/Keston.Smith/RWPS/RWPSLakes/Winnipesaukee.NWPS.WW3.msh') +gW.x=gW.x-360; +gO=loadmshWW3('/scratch3/NCEPDEV/climate/Keston.Smith/RWPS/RWPSLakes/Okeechobee.NWPS.WW3.msh') +gO.x=gO.x-360; + +g=CombineMesh(g,gO); +g=CombineMesh(g,gS); +g=CombineMesh(g,gW); + +x=g.x;y=g.y;z=g.z;e=g.e; + +if isplot, + LS=ComputeLengthScale_wgs84_MEL(x,y,e);LSn=Ele2Nodes(x,y,e,LS); + clf;ph=patch(x(e'),y(e'),LSn(e'));cm=colormap('jet');shading interp;axis equal; + caxis([0,12]) + colormap(flip(cm)); +end + +WriteWW3MeshX(g,'RWPS.WW3a.lakes.msh'); + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%% STEP (C) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%clear +g=loadmshWW3('RWPS.WW3a.lakes.msh') +%Remove key islands remaining in mesh that are present in the PSLG file + +p=loadmsh(pslgfile); + +p.x=p.point.coord(:,1); +p.y=p.point.coord(:,2); +p.edges=p.edge2.index(:,1:2); +p.x=p.x-360; +x=g.x;y=g.y;z=g.z;e=g.e; + +if isplot, + clf;ph=patch(x(e'),y(e'),z(e'));cm=colormap('jet');shading interp;axis equal; + hold on + plot(p.x,p.y,'k.') +end + +MinBndDist=1000; +gnew=g; + +%go through all longitude and remove nodes +dx=2; +dxb=10 +%close all +RemoveIsl=1; +n=1 +if RemoveIsl, + for xx=(ceil(min(g.x))+dxb):dx:(floor(max(g.x))-dxb) + ax11=[xx-dx,xx+dx,min(g.y)+dxb,max(g.y)-dxb] + N(n)=length(gnew.x); + AX(n,:)=ax11; + gnew=RemoveMissingIslandsEle(gnew,ax11,p,MinBndDist,0); + n=n+1; + n/243 + length(gnew.x) + %figure(3);clf;plot(N,'ko-');pause(.001) + end +% figure(3);clf;plot(AX(:,1),N,'ko-'); +end + + +g=gnew + +%%%%%%%%%%%%%%%%%%%%%%%%%%%% STEP (D) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +g=RemoveSandPointsWW3(g,'RWPS.WW3a1.lakes.msh') + +%%%%%%%%%%%%%%%%%%%%%%%%%%%% STEP (E) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +p=loadmsh(pslgfile); +p.x=p.point.coord(:,1);p.x=p.x-360;p.y=p.point.coord(:,2);p.edges=p.edge2.index(:,1:2); + +x=g.x;y=g.y;z=g.z;e=g.e; +if isplot, + LS=ComputeLengthScale_wgs84_MEL(x,y,e);LSn=Ele2Nodes(x,y,e,LS); + clf;ph=patch(x(e'),y(e'),LSn(e'));cm=colormap('jet');shading interp;axis equal; + caxis([0,12]) + colormap(flip(cm)); + hold on; + plot(p.x,p.y,'k.'); +end + +p=loadmsh(pslgfile); +p.x=p.point.coord(:,1);p.x=p.x-360;p.y=p.point.coord(:,2);p.edges=p.edge2.index(:,1:2); + +g0=g; +gnew=g +if isplot + axAS =[ -179.6738 -159.5513 -18.4086 -7.1731] + axis(axAS);pause(5) + + axTofol =[ -197.5575 -196.5479 4.8724 5.7902] + axis(axTofol);pause(5) + + axPA = [-177.6646 -175.5927 -0.2906 1.3435] + axis(axPA);pause(5) + + axJA =[ -172.1141 -165.7656 14.1706 19.1777] + axis(axJA);pause(5) + + axMWI =[ -179.5965 -176.0019 27.0046 29.8397] + axis(axMWI);pause(5) + + axTin=[-215.1401 -213.4623 14.3922 15.7155] + axis(axTin);pause(5) + + axNNMI =[ -215.9834 -213.8052 19.3525 21.0704]; + axis(axNNMI);pause(5) + + axNNMI1=[-214.8924 -213.6875 14.6480 15.5983]; + axis(axNNMI1);pause(5) + + axMaj =[ -177.2629 -175.7651 -0.1340 1.0474] + axis(axMaj);pause(5) + + axHI =[ -179.1073 -153.7595 11.4935 31.4855] + axis(axHI);pause(5) + + axChuuk =[ -198.1190 -196.0417 4.7967 5.8354] + axis(axChuuk);pause(5) +end + +WriteWW3MeshX(gnew,'RWPS.WW3b.lakes.msh') + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%% STEP (F) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%fix new orleans +%clear +g=loadmshWW3('RWPS.WW3b.lakes.msh') +%Examine and Hand Edit Mesh around new orleans to relect changes if nescesary +%Should not be nescesary with +if isplot, + x=g.x;y=g.y;z=g.z;e=g.e; + LS=ComputeLengthScale_wgs84_MEL(x,y,e); + LSn=Ele2Nodes(x,y,e,LS); + clf;ph=patch(x(e'),y(e'),LSn(e'));cm=colormap('jet');shading interp;axis equal; + colormap(flip(cm)); + caxis([0,12]); + % Shape file of new marine zones in New Orleans + S=shaperead('/scratch3/NCEPDEV/climate/Keston.Smith/RWPS/Data/us_coastline/mz03mr26_LIX.shp') + hold on + for k=1:length(S),plot(S(k).X,S(k).Y,'k');end + + axNOZ =[ -91.6701 -87.7499 28.7284 30.9173] + axis(axNOZ) + gnew=g; + eno=input('Enter 1 if you want to edit the mesh around New Orleans:' ) + if eno, + gnew=RemoveMeshParts(g,axNOZ,S,[0,50]); + else + gnew=g; + end +else + gnew=g;% no edit +end + +WriteWW3MeshX(gnew,'RWPS.WW3c.lakes.msh') + +%confirm no introduction of sand points +%%%%%%%%%%%%%%%%%%%%%%%%%%%% STEP (G) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +g=RemoveSandPointsWW3(gnew,'RWPS.WW3d.lakes.msh') + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%% STEP (H) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%Fix boundary warping from projection in mesh generation +%clear +close all +g=loadmshWW3('RWPS.WW3d.lakes.msh') +g0=g; + +Blon=[129.91 10.71] +Blat=[-30.42 79.99] +Blon(1)=Blon(1)-360 +x=g.x;y=g.y;z=g.z;e=g.e; +jb=g.bnd; +bnd0=detbndy(g.e) + +if isplot, + clf;ph=patch(x(e'),y(e'),z(e')); + cm=colormap('jet');shading interp;hold on + colormap(flip(cm)); + axis equal;hold on + plot(g.x(jb),g.y(jb),'k.');hold on +end +js=find(g.y(jb)max(Blat)); +g.y(jb(js))=max(Blat); +if isplot,plot(g.x(jb(js)),g.y(jb(js)),'r.');end + +%dx=degree latitude delta to discriminate land points near boundary from open ocean boundary +dx=.05; +js=find(and( g.x(jb)>min(Blon),g.x(jb)max(Blon)-dx )); +g.x(jb(js))=max(Blon); +if isplot,plot(g.x(jb(js)),g.y(jb(js)),'r.');end +WriteWW3MeshX(g,'RWPS.WW3e.lakes.msh') + +%confirm no introduction of sand points +%%%%%%%%%%%%%%%%%%%%%%%%%%%% STEP (I) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +g0=RemoveSandPointsWW3(g,'RWPS.WW3f.lakes.msh') + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%% STEP (J) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +WriteWW3MeshX(g0,'RWPS.WW3g.lakes.msh') + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%Some Plotting +if isplot, + g=g0; + + + %clear + g=loadmshWW3('RWPS.WW3g.lakes.msh')%RWPS.PIXAllLnwps.PP.WW3c.msh'); + x=g.x;y=g.y;z=g.z;e=g.e; + LS=ComputeLengthScale_wgs84_MEL(x,y,e);LSn=Ele2Nodes(x,y,e,LS); + clf;ph=patch(x(e'),y(e'),LSn(e'));cm=colormap('jet');shading interp;axis equal; + jb=g.bnd; + hold on; + plot(g.x(jb),g.y(jb),'k.');hold on + + + f=z; + clf;patch(x(e'),y(e'),f(e'));cm=colormap('jet');shading interp;axis equal; + colormap(flip(cm)); + caxis([0,6000]); + kprint('Bathy.jpg'); + + clf; + caxis([0,6000]); + colormap(flip(cm)); + + hcb=colorbar('h','position',[.1,.075,.8,.025]) + %hcb.Label.String='(km)' + set(gca,'visible','off') + kprint('BathyColorbarH.jpg'); + + clf; + colorbar; + colormap(flip(cm)); + caxis([0,6000]); + kprint('BathyColorbarV.jpg'); + + + LS=ComputeLengthScale_wgs84_MEL(x,y,e); + LSn=Ele2Nodes(x,y,e,LS); + + clf;patch(x(e'),y(e'),LSn(e'));cm=colormap('jet');shading interp;axis equal; + colormap(flip(cm)); + caxis([0,12]); + kprint('Lengthscale.jpg'); + + + axCAR =[ -85.4065 -61.7227 8.8200 26.8661] + ax=axCAR + axis(ax);daspect([1,cos(pi*ax(3)/180),1]) + kprint('Caribean.jpg') + + + axAK =[ -192.1450 -152.2804 47.2479 77.6231] + ax=axAK + axis(ax);daspect([1,cos(pi*ax(3)/180),1]) + kprint('Alaska.jpg') + + axHI =[ -179.5950 -152.9066 13.8641 34.1996] + ax=axHI + axis(ax);daspect([1,cos(pi*ax(3)/180),1]) + kprint('Hawaii.jpg') + + + axMI =[ -220.2161 -208.6314 12.5003 21.3274]; + ax=axMI + axis(ax);daspect([1,cos(pi*ax(3)/180),1]) + kprint('MarianaIslands.jpg') + + axAS =[ -173.3623 -168.3684 -16.4846 -12.0090] + ax=axAS + axis(ax);daspect([1,cos(pi*ax(3)/180),1]) + kprint('AmericanSamoa.jpg') + + + axMicro =[ -210.3818 -185.8701 -3.7494 15.5832] + ax=axMicro + axis(ax);daspect([1,cos(pi*ax(3)/180),1]) + kprint('Micronesia.jpg') + + axWMicro =[ -226.6896 -214.3594 5.3753 15.1002] + ax=axWMicro + axis(ax);daspect([1,cos(pi*ax(3)/180),1]) + kprint('WestMicronesia.jpg') + + + figure; + clf; + caxis([0,12]); + colormap(flip(cm)); + + hcb=colorbar('h','position',[.1,.075,.8,.025]) + hcb.Label.String='(km)' + set(gca,'visible','off') + kprint('LengthScaleColorbarH.jpg'); + + clf + caxis([0,12]); + colormap(flip(cm)); + + % hcb=colorbar('v','position',[.1,.075,.8,.025]) + vcb=colorbar('v','position',[.075,.1,.025,.8]) + vcb.Label.String='(km)' + set(gca,'visible','off') + kprint('LengthScaleColorbarV.jpg'); + + + clf + caxis([0,6000]); + colormap(flip(cm)); + + hcb=colorbar('h','position',[.1,.075,.8,.025]) + hcb.Label.String='(m)' + set(gca,'visible','off') + kprint('BathyColorbarH.jpg'); + + clf + caxis([0,6000]); + colormap(flip(cm)); + + % hcb=colorbar('v','position',[.1,.075,.8,.025]) + vcb=colorbar('v','position',[.075,.1,.025,.8]) + vcb.Label.String='(m)' + set(gca,'visible','off') + kprint('BathyColorbarV.jpg'); + +end diff --git a/unst_msh_gen/regional/MeshGenTemplateDirectory/PreProcessCoastline.m b/unst_msh_gen/regional/MeshGenTemplateDirectory/PreProcessCoastline.m new file mode 100644 index 0000000..9af823d --- /dev/null +++ b/unst_msh_gen/regional/MeshGenTemplateDirectory/PreProcessCoastline.m @@ -0,0 +1,22 @@ + + +SetPath + +MakeCoastalBoundariesGSHHS +%BuildBoundaryPSLGwGSHHS +%MakeDfunGSHHS +SetPath +lonWest=129.91;lonEast=10.71;latSouth=-30.42;latNorth=79.99; +CoastLineFile = 'GlobalCoastlineGSHHS.shp' +BuildBoundaryPSLGfunction(CoastLineFile,lonWest,lonEast,latSouth,latNorth) + +%TargetShap=uscl; %Shapefile with points of interest +%DX=.1; %lat/lon grid resolution for jigsaw inputs +%[xpi,ypi]=ExtraPointsOfIntrest; %Define points of interest for distance function not in TargetShape file +%MakeDistanceToCoastData(DX,TargetShape,xpi,ypi) %use for local meshes that don't span the international dateline + +%use this for RWPS to deal with international dateline discontinuity. +MakeDistanceToCoastRWPS + + + diff --git a/unst_msh_gen/regional/MeshGenTemplateDirectory/PreProcessCoastline_NWCoastalMesh.m b/unst_msh_gen/regional/MeshGenTemplateDirectory/PreProcessCoastline_NWCoastalMesh.m new file mode 100644 index 0000000..574d7d2 --- /dev/null +++ b/unst_msh_gen/regional/MeshGenTemplateDirectory/PreProcessCoastline_NWCoastalMesh.m @@ -0,0 +1,12 @@ + +SetPath + +MakeCoastalBoundariesOSM +lonWest=-128;lonEast=-121; +latSouth=45;latNorth=51; +CoastLineFile = 'GlobalCoastlineOSM.shp' +BuildBoundaryPSLGfunction(CoastLineFile,lonWest,lonEast,latSouth,latNorth,PSLGfile) +%[xpi,ypi]=ExtraPointsOfIntrest; +TargetShape=uscl;% US coastline as target for distance +DX=.0125; +MakeDistanceToCoastData(DX,uscl,[],[]) diff --git a/unst_msh_gen/regional/MeshGenTemplateDirectory/RWPS.ng.NoFiltrsOpts.py b/unst_msh_gen/regional/MeshGenTemplateDirectory/RWPS.ng.NoFiltrsOpts.py new file mode 100644 index 0000000..6b5c853 --- /dev/null +++ b/unst_msh_gen/regional/MeshGenTemplateDirectory/RWPS.ng.NoFiltrsOpts.py @@ -0,0 +1,256 @@ + +import os +import argparse +import time +import numpy as np +import netCDF4 as nc + +import jigsawpy + +from scipy.interpolate import RegularGridInterpolator + +#-------------------Input Files---------------------------------------- +PSLGFile="GlobalCoastlineOSM.PSLG.msh" +DistanceToCoastFile="DFun.GlobalCoastlineOSM.PSLG.msh" +TopographyFile="Topo.DFun.GlobalCoastlineOSM.PSLG.msh" + +PSLGFile="PSLGboundaryGSHHS.msh" +DistanceToCoastFile="GlobalCoastlineGSHHS.PSLG.msh" +TopographyFile="DFun.GlobalCoastlineGSHHS.PSLG.msh" + +#jigsaw .msh format Planer Straight Line Graph defining mesh outer boundary and coastline +PSLGFile="PSLGboundaryOSMxGSHHS.BOXES.msh" +#jigsaw gridded .msh format Distance to taget poings +DistanceToCoastFile="DFun.PSLGboundaryOSMxGSHHS.BOXES.msh" +#jigsaw gridded .msh format topography on same grid as distance +TopographyFile="Topo.DFun.PSLGboundaryOSMxGSHHS.BOXES.msh" + +# directory to write output files to +OutDir='testOSMxGSHHS.BOXES/' + +ww3_mesh_file='RWPS.OSMxGSHHS.BOXES.msh' + +#-------------------Paramter Inputs------------------------------------ +#parameters for specifying resolution +d0=10000. +d1=320000. - d0 +beta=1000. +Smin=0.5 +Smax=10. + +#-------------------Main Program--------------------------------------- + +# Create the output directory------------------------------------------ +try: + os.mkdir(OutDir) + print(f"Directory '{OutDir}' created successfully.") +except FileExistsError: + print(f"Directory '{OutDir}' already exists. Proceeding ...") +except PermissionError: + print(f"Permission denied: Unable to create '{OutDir}'.") + +# Setup jigsaw structures---------------------------------------------- +opts = jigsawpy.jigsaw_jig_t() +topo = jigsawpy.jigsaw_msh_t() #topographic database +dist = jigsawpy.jigsaw_msh_t() #distance to US coastline in meters +geom = jigsawpy.jigsaw_msh_t() +mesh = jigsawpy.jigsaw_msh_t() +meshR3 = jigsawpy.jigsaw_msh_t() +hmat = jigsawpy.jigsaw_msh_t() +proj = jigsawpy.jigsaw_prj_t() + +opts.geom_file = "geom.msh" +opts.jcfg_file = "aust.jig" +opts.mesh_file = "mesh.msh" +opts.hfun_file = "spac.msh" + +# load input data------------------------------------------------------ +jigsawpy.loadmsh(PSLGFile, geom) +jigsawpy.loadmsh(DistanceToCoastFile, dist) +jigsawpy.loadmsh(TopographyFile, topo) + +# truncate data to bounding rectangle of input PSLG-------------------- +xmin = np.min( geom.point["coord"][:, 0]) +ymin = np.min( geom.point["coord"][:, 1]) +xmax = np.max( geom.point["coord"][:, 0]) +ymax = np.max( geom.point["coord"][:, 1]) + +tv = topo.value +dv = dist.value + +xmsk = np.logical_and( topo.xgrid > xmin , topo.xgrid < xmax ) +ymsk = np.logical_and( topo.ygrid > ymin , topo.ygrid < ymax ) + +tv = tv[:, xmsk] +tv = tv[ymsk, :] +dv = dv[:, xmsk] +dv = dv[ymsk, :] + +# define spatial resolution shape function----------------------------- +W=np.exp(- (np.abs(dv-d0) / d1) - (np.abs(tv) / beta) ) +W[ np.where(dv < d0 ) ] = 1. + +# build resolution specification gridded data structure, hmat---------- +hmat=topo +hmat.value=Smax - (Smax - Smin)*W + +hmat.mshID = "ellipsoid-grid" +hmat.radii = np.full( +3, +6371.0, dtype=jigsawpy.jigsaw_msh_t.REALS_t) + +hmat.xgrid = hmat.xgrid[xmsk] * np.pi / 180. +hmat.ygrid = hmat.ygrid[ymsk] * np.pi / 180. + +hmat.value = np.maximum(hmat.value, Smin)#should not be nescesary +hmat.value = np.minimum(hmat.value, Smax) + +hmat.slope = np.full( hmat.value.shape, +0.1500 , dtype=jigsawpy.jigsaw_msh_t.REALS_t) + +#------------------------------------ do stereographic proj. +geom.point["coord"][:, :] *= np.pi / 180. + +proj.prjID = 'stereographic' +proj.radii = +6.371E+003 +proj.xbase = +0.500 * (xmin + xmax) * np.pi / 180. +proj.ybase = +0.500 * (ymin + ymax) * np.pi / 180. + +jigsawpy.savemsh(OutDir+"HFUN.msh", hmat) + +jigsawpy.project(geom, proj, "fwd") +jigsawpy.project(hmat, proj, "fwd") + +jigsawpy.savemsh(opts.geom_file, geom) +jigsawpy.savemsh(opts.hfun_file, hmat) + +# save hmat------------------------------------------------------------ +jigsawpy.savemsh(OutDir+"HFUNproj0.msh", hmat) + +#smooth hmat +jigsawpy.cmd.marche(opts, hmat) + +# save smoothed hmat--------------------------------------------------- +jigsawpy.savemsh(OutDir+"HFUNproj1.msh", hmat) + +# make mesh using JIGSAW----------------------------------------------- +opts.hfun_scal = "absolute" +opts.hfun_hmax = float("inf") # null HFUN limits +opts.hfun_hmin = float(+0.00) +#opts.hfun_hmax = 1.25*Smax # Unintended effects, better off null +#opts.hfun_hmin = .5*Smin + +opts.mesh_dims = +2 # 2-dim. simplexes +opts.mesh_eps1 = +1. + +#opts.mesh_top1 = "true" !!!No convergece + + +ttic = time.time() + +jigsawpy.cmd.jigsaw(opts, mesh) + +ttoc = time.time() + +print("CPUSEC =", (ttoc - ttic)) + +# save mesh in JIGSAW native projection based on input PSLG------------ +jigsawpy.savemsh(OutDir+"/RWPS.PROJ.msh",mesh) + +# compute costa functions and save +cost = jigsawpy.triscr2(mesh.point["coord"],mesh.tria3["index"]) +np.savetxt(OutDir+"TriScr2.txt",cost,"%f") +print("TRISCR =", np.min(cost), np.mean(cost)) + +cost = jigsawpy.pwrscr2(mesh.point["coord"], mesh.power, mesh.tria3["index"]) +np.savetxt(OutDir+"PwrScr2.txt",cost,"%f") +print("PWRSCR =", np.min(cost), np.mean(cost)) + +tbad = jigsawpy.centre2(mesh.point["coord"],mesh.power,mesh.tria3["index"]) +print("OBTUSE =",+np.count_nonzero(np.logical_not(tbad))) + + +# project mesh nodes to radian lat, lon +jigsawpy.project(mesh, proj, "inv") # This used to work +#jigsawpy.savemsh("RWPS.radian.msh",mesh) + +# transform mesh nodes to degree lat, lon +mesh.point["coord"][:, :] = mesh.point["coord"][:, :]*180. / np.pi + +# save mesh in degree lat, lon system +jigsawpy.savemsh(OutDir+"RWPS.LL.msh",mesh) + +# create jigsaw R3 mesh on global surface and save to +S2=mesh.point["coord"][:,[0,1]] +S2=S2*np.pi/180. + +R3=jigsawpy.S2toR3(mesh.radii,S2) +#np.savetxt("R3.txt",R3," %f ") # save 3D nodes + +meshR3 = jigsawpy.jigsaw_msh_t() +mesh.mshID = 'ellipsoid-mesh' +meshR3.tria3=mesh.tria3 +meshR3.ndims=3 +#make 3D coordinates +nd=R3.shape +meshR3.vert3 = np.zeros(nd[0], dtype=mesh.VERT3_t) +meshR3.vert3["coord"] = R3 +jigsawpy.savemsh(OutDir+"RWPS.R3.msh",meshR3) + + +# Now apply filters and output in WW3 form +# import FilterRoutines.py + +from FilterRoutinesNM import * + +#replace mesh with R3 mesh, mesh->mesh R3 +jigsawpy.loadmsh(OutDir+"RWPS.R3.msh", mesh) # uncomment if starting here + +opts.geom_file = "geom.msh" #saves the geometry info for jigsaw +opts.jcfg_file = "opts.jig" #jigsaw ctlr file + + +geom.mshID = "ellipsoid-mesh" +geom.radii = np.full(3, 6.371E+003, dtype=geom.REALS_t) + +jigsawpy.savemsh(opts.geom_file, geom) + +inject_dem() +filter_ocn() + +jigsawpy.savemsh(OutDir+"RWPS.F.R3.msh", mesh) + +# viz. in eg. paraview +jigsawpy.savevtk(OutDir+"test.vtk", mesh) + +# convert to lon lat +point = mesh.point["coord"] +point = jigsawpy.R3toS2(geom.radii, point) +point*= 180. / np.pi + +depth = np.reshape(-1*mesh.value, (mesh.value.size, 1)) +depth[depth <= 0] = 2 +point = np.hstack((point, depth)) # append elev. as 3rd coord. +cells = [("triangle", mesh.tria3["index"])] +tri_data=cells[0][1]+1 + +#put coordinates in non standard format to avoid international date line +lon=point[:,0] +lon[np.where(lon>90)]=lon[np.where(lon>90)]-360 +point[:,0]=lon + +write_gmsh_mesh(OutDir+"RWPS.ww3", point, tri_data) + +mesh.point["coord"]=point +jigsawpy.savemsh(OutDir+"RWPS.F.LLH.msh", mesh) + + +#write final mesh in jigsaw .msh format +meshR2 = jigsawpy.jigsaw_msh_t() +#make 2D coordinates +nd=point.shape +meshR2.ndims=2 +meshR2.vert2 = np.zeros(nd[0], dtype=mesh.VERT2_t) +meshR2.vert2["coord"] = point[:,[0,1]] +meshR2.tria3=mesh.tria3 +meshR2.mshID=mesh.mshID + +jigsawpy.savemsh(OutDir+"RWPS.F.LL.msh", meshR2) + diff --git a/unst_msh_gen/regional/MeshGenTemplateDirectory/RWPSMeshGenScript.GSHHS.py b/unst_msh_gen/regional/MeshGenTemplateDirectory/RWPSMeshGenScript.GSHHS.py new file mode 100644 index 0000000..e96fa91 --- /dev/null +++ b/unst_msh_gen/regional/MeshGenTemplateDirectory/RWPSMeshGenScript.GSHHS.py @@ -0,0 +1,267 @@ + +import os +import argparse +import time +import numpy as np +import netCDF4 as nc + +import jigsawpy + +from scipy.interpolate import RegularGridInterpolator + +#-------------------Input Files---------------------------------------- + +""" +#OSM coastline +PSLGFile="GlobalCoastlineOSM.PSLG.msh" +DistanceToCoastFile="DFun.GlobalCoastlineOSM.PSLG.msh" +TopographyFile="Topo.DFun.GlobalCoastlineOSM.PSLG.msh" +""" + +#GSSH coastline +#jigsaw .msh format Planer Straight Line Graph defining mesh outer boundary and coastline +PSLGFile="GlobalCoastlineGSHHS.PSLG.msh" +#jigsaw gridded .msh format Distance to taget poings +DistanceToCoastFile="DFun.GlobalCoastlineGSHHS.PSLG.msh" +#jigsaw gridded .msh format topography on same grid as distance +TopographyFile="DFun.GlobalCoastlineGSHHS.PSLG.msh" + +""" +# GSHHS except identified problem spots (i.e. American Samoa) where OSM is used +#jigsaw .msh format Planer Straight Line Graph defining mesh outer boundary and coastline +PSLGFile="PSLGboundaryOSMxGSHHS.BOXES.msh" +#jigsaw gridded .msh format Distance to taget poings +DistanceToCoastFile="DFun.PSLGboundaryOSMxGSHHS.BOXES.msh" +#jigsaw gridded .msh format topography on same grid as distance +TopographyFile="Topo.DFun.PSLGboundaryOSMxGSHHS.BOXES.msh" +""" + +# directory to write output files to +OutDir='testRWPS.GSHHS/' + +ww3_mesh_file='RWPS.GSHHS.WW3.msh' + +#-------------------Paramter Inputs------------------------------------ +#parameters for specifying resolution +d0=10000. +d1=320000. - d0 +beta=1000. +Smin=0.5 +Smax=10. + +#-------------------Main Program--------------------------------------- + +# Create the output directory------------------------------------------ +try: + os.mkdir(OutDir) + print(f"Directory '{OutDir}' created successfully.") +except FileExistsError: + print(f"Directory '{OutDir}' already exists. Proceeding ...") +except PermissionError: + print(f"Permission denied: Unable to create '{OutDir}'.") + +# Setup jigsaw structures---------------------------------------------- +opts = jigsawpy.jigsaw_jig_t() +topo = jigsawpy.jigsaw_msh_t() #topographic database +dist = jigsawpy.jigsaw_msh_t() #distance to US coastline in meters +geom = jigsawpy.jigsaw_msh_t() +mesh = jigsawpy.jigsaw_msh_t() +meshR3 = jigsawpy.jigsaw_msh_t() +hmat = jigsawpy.jigsaw_msh_t() +proj = jigsawpy.jigsaw_prj_t() + +opts.geom_file = "geom.msh" +opts.jcfg_file = "jcfg.jig" +opts.mesh_file = "mesh.msh" +opts.hfun_file = "spac.msh" + +# load input data------------------------------------------------------ +jigsawpy.loadmsh(PSLGFile, geom) +jigsawpy.loadmsh(DistanceToCoastFile, dist) +jigsawpy.loadmsh(TopographyFile, topo) + +# truncate data to bounding rectangle of input PSLG-------------------- +xmin = np.min( geom.point["coord"][:, 0]) +ymin = np.min( geom.point["coord"][:, 1]) +xmax = np.max( geom.point["coord"][:, 0]) +ymax = np.max( geom.point["coord"][:, 1]) + +tv = topo.value +dv = dist.value + +xmsk = np.logical_and( topo.xgrid > xmin , topo.xgrid < xmax ) +ymsk = np.logical_and( topo.ygrid > ymin , topo.ygrid < ymax ) + +tv = tv[:, xmsk] +tv = tv[ymsk, :] +dv = dv[:, xmsk] +dv = dv[ymsk, :] + +# define spatial resolution shape function----------------------------- +W=np.exp(- (np.abs(dv-d0) / d1) - (np.abs(tv) / beta) ) +W[ np.where(dv < d0 ) ] = 1. + +# build resolution specification gridded data structure, hmat---------- +hmat=topo +hmat.value=Smax - (Smax - Smin)*W + +hmat.mshID = "ellipsoid-grid" +hmat.radii = np.full( +3, +6371.0, dtype=jigsawpy.jigsaw_msh_t.REALS_t) + +hmat.xgrid = hmat.xgrid[xmsk] * np.pi / 180. +hmat.ygrid = hmat.ygrid[ymsk] * np.pi / 180. + +hmat.value = np.maximum(hmat.value, Smin)#should not be nescesary +hmat.value = np.minimum(hmat.value, Smax) + +hmat.slope = np.full( hmat.value.shape, +0.1500 , dtype=jigsawpy.jigsaw_msh_t.REALS_t) + +#------------------------------------ do stereographic proj. +geom.point["coord"][:, :] *= np.pi / 180. + +proj.prjID = 'stereographic' +proj.radii = +6.371E+003 +proj.xbase = +0.500 * (xmin + xmax) * np.pi / 180. +proj.ybase = +0.500 * (ymin + ymax) * np.pi / 180. + +jigsawpy.savemsh(OutDir+"HFUN.msh", hmat) + +jigsawpy.project(geom, proj, "fwd") +jigsawpy.project(hmat, proj, "fwd") + +jigsawpy.savemsh(opts.geom_file, geom) +jigsawpy.savemsh(opts.hfun_file, hmat) + +# save hmat------------------------------------------------------------ +jigsawpy.savemsh(OutDir+"HFUNproj0.msh", hmat) + +#smooth hmat +jigsawpy.cmd.marche(opts, hmat) + +# save smoothed hmat--------------------------------------------------- +jigsawpy.savemsh(OutDir+"HFUNproj1.msh", hmat) + +# make mesh using JIGSAW----------------------------------------------- +opts.hfun_scal = "absolute" +opts.hfun_hmax = float("inf") # null HFUN limits +opts.hfun_hmin = float(+0.00) +#opts.hfun_hmax = 1.25*Smax # Unintended effects, better off null +#opts.hfun_hmin = .5*Smin + +opts.mesh_dims = +2 # 2-dim. simplexes +opts.mesh_eps1 = +1. + +#opts.mesh_top1 = "true" !!!No convergece + + +ttic = time.time() + +jigsawpy.cmd.jigsaw(opts, mesh) + +ttoc = time.time() + +print("CPUSEC =", (ttoc - ttic)) + +# save mesh in JIGSAW native projection based on input PSLG------------ +jigsawpy.savemsh(OutDir+"/RWPS.PROJ.msh",mesh) + +# compute costa functions and save +cost = jigsawpy.triscr2(mesh.point["coord"],mesh.tria3["index"]) +np.savetxt(OutDir+"TriScr2.txt",cost,"%f") +print("TRISCR =", np.min(cost), np.mean(cost)) + +cost = jigsawpy.pwrscr2(mesh.point["coord"], mesh.power, mesh.tria3["index"]) +np.savetxt(OutDir+"PwrScr2.txt",cost,"%f") +print("PWRSCR =", np.min(cost), np.mean(cost)) + +tbad = jigsawpy.centre2(mesh.point["coord"],mesh.power,mesh.tria3["index"]) +print("OBTUSE =",+np.count_nonzero(np.logical_not(tbad))) + + +# project mesh nodes to radian lat, lon +jigsawpy.project(mesh, proj, "inv") # This used to work +#jigsawpy.savemsh("RWPS.radian.msh",mesh) + +# transform mesh nodes to degree lat, lon +mesh.point["coord"][:, :] = mesh.point["coord"][:, :]*180. / np.pi + +# save mesh in degree lat, lon system +jigsawpy.savemsh(OutDir+"RWPS.LL.msh",mesh) + +# create jigsaw R3 mesh on global surface and save to +S2=mesh.point["coord"][:,[0,1]] +S2=S2*np.pi/180. + +R3=jigsawpy.S2toR3(mesh.radii,S2) +#np.savetxt("R3.txt",R3," %f ") # save 3D nodes + +meshR3 = jigsawpy.jigsaw_msh_t() +mesh.mshID = 'ellipsoid-mesh' +meshR3.tria3=mesh.tria3 +meshR3.ndims=3 +#make 3D coordinates +nd=R3.shape +meshR3.vert3 = np.zeros(nd[0], dtype=mesh.VERT3_t) +meshR3.vert3["coord"] = R3 +jigsawpy.savemsh(OutDir+"RWPS.R3.msh",meshR3) + + +# Now apply filters and output in WW3 form +# import FilterRoutines.py + +from FilterRoutinesNM import * + +#replace mesh with R3 mesh, mesh->mesh R3 +jigsawpy.loadmsh(OutDir+"RWPS.R3.msh", mesh) # uncomment if starting here + +opts.geom_file = "geom.msh" #saves the geometry info for jigsaw +opts.jcfg_file = "opts.jig" #jigsaw ctlr file + + +geom.mshID = "ellipsoid-mesh" +geom.radii = np.full(3, 6.371E+003, dtype=geom.REALS_t) + +jigsawpy.savemsh(opts.geom_file, geom) + +inject_dem() +filter_ocn() + +jigsawpy.savemsh(OutDir+"RWPS.F.R3.msh", mesh) + +# viz. in eg. paraview +jigsawpy.savevtk(OutDir+"test.vtk", mesh) + +# convert to lon lat +point = mesh.point["coord"] +point = jigsawpy.R3toS2(geom.radii, point) +point*= 180. / np.pi + +depth = np.reshape(-1*mesh.value, (mesh.value.size, 1)) +depth[depth <= 0] = 2 +point = np.hstack((point, depth)) # append elev. as 3rd coord. +cells = [("triangle", mesh.tria3["index"])] +tri_data=cells[0][1]+1 + +#put coordinates in non standard format to avoid international date line +lon=point[:,0] +lon[np.where(lon>90)]=lon[np.where(lon>90)]-360 +point[:,0]=lon + +write_gmsh_mesh(OutDir+"RWPS.ww3", point, tri_data) + +mesh.point["coord"]=point +jigsawpy.savemsh(OutDir+"RWPS.F.LLH.msh", mesh) + + +#write final mesh in jigsaw .msh format +meshR2 = jigsawpy.jigsaw_msh_t() +#make 2D coordinates +nd=point.shape +meshR2.ndims=2 +meshR2.vert2 = np.zeros(nd[0], dtype=mesh.VERT2_t) +meshR2.vert2["coord"] = point[:,[0,1]] +meshR2.tria3=mesh.tria3 +meshR2.mshID=mesh.mshID + +jigsawpy.savemsh(OutDir+"RWPS.F.LL.msh", meshR2) + diff --git a/unst_msh_gen/regional/MeshGenTemplateDirectory/RWPSMeshGenScript.NWcoastal.py b/unst_msh_gen/regional/MeshGenTemplateDirectory/RWPSMeshGenScript.NWcoastal.py new file mode 100644 index 0000000..a54a248 --- /dev/null +++ b/unst_msh_gen/regional/MeshGenTemplateDirectory/RWPSMeshGenScript.NWcoastal.py @@ -0,0 +1,248 @@ + +import os +import argparse +import time +import numpy as np +import netCDF4 as nc + +import jigsawpy + +from scipy.interpolate import RegularGridInterpolator + +#-------------------Input Files---------------------------------------- + +#GSSH coastline +#jigsaw .msh format Planer Straight Line Graph defining mesh outer boundary and coastline +PSLGFile="NWcoastal.PSLG.msh" +#jigsaw gridded .msh format Distance to taget poings +DistanceToCoastFile="DFun.NWcoastal.PSLG.msh" +#jigsaw gridded .msh format topography on same grid as distance +TopographyFile="Topo.DFun.NWcoastal.PSLG.msh" + +# directory to write output files to +OutDir='NWcoastal/' + +#-------------------Paramter Inputs------------------------------------ +#parameters for specifying resolution +d0=25000. +d1=320000. - d0 +beta=250. +Smin=0.25 +Smax=7. + +#-------------------Main Program--------------------------------------- + +# Create the output directory------------------------------------------ +try: + os.mkdir(OutDir) + print(f"Directory '{OutDir}' created successfully.") +except FileExistsError: + print(f"Directory '{OutDir}' already exists. Proceeding ...") +except PermissionError: + print(f"Permission denied: Unable to create '{OutDir}'.") + +# Setup jigsaw structures---------------------------------------------- +opts = jigsawpy.jigsaw_jig_t() +topo = jigsawpy.jigsaw_msh_t() #topographic database +dist = jigsawpy.jigsaw_msh_t() #distance to US coastline in meters +geom = jigsawpy.jigsaw_msh_t() +mesh = jigsawpy.jigsaw_msh_t() +meshR3 = jigsawpy.jigsaw_msh_t() +hmat = jigsawpy.jigsaw_msh_t() +proj = jigsawpy.jigsaw_prj_t() + +opts.geom_file = "geom.msh" +opts.jcfg_file = "jcfg.jig" +opts.mesh_file = "mesh.msh" +opts.hfun_file = "spac.msh" + +# load input data------------------------------------------------------ +jigsawpy.loadmsh(PSLGFile, geom) +jigsawpy.loadmsh(DistanceToCoastFile, dist) +jigsawpy.loadmsh(TopographyFile, topo) + +# truncate data to bounding rectangle of input PSLG-------------------- +xmin = np.min( geom.point["coord"][:, 0]) +ymin = np.min( geom.point["coord"][:, 1]) +xmax = np.max( geom.point["coord"][:, 0]) +ymax = np.max( geom.point["coord"][:, 1]) + +tv = topo.value +dv = dist.value + +xmsk = np.logical_and( topo.xgrid > xmin , topo.xgrid < xmax ) +ymsk = np.logical_and( topo.ygrid > ymin , topo.ygrid < ymax ) + +tv = tv[:, xmsk] +tv = tv[ymsk, :] +dv = dv[:, xmsk] +dv = dv[ymsk, :] + +# define spatial resolution shape function----------------------------- +W=np.exp(- (np.abs(dv-d0) / d1) - (np.abs(tv) / beta) ) +W[ np.where(dv < d0 ) ] = 1. + +# build resolution specification gridded data structure, hmat---------- +hmat=topo +hmat.value=Smax - (Smax - Smin)*W + +hmat.mshID = "ellipsoid-grid" +hmat.radii = np.full( +3, +6371.0, dtype=jigsawpy.jigsaw_msh_t.REALS_t) + +hmat.xgrid = hmat.xgrid[xmsk] * np.pi / 180. +hmat.ygrid = hmat.ygrid[ymsk] * np.pi / 180. + +hmat.value = np.maximum(hmat.value, Smin)#should not be nescesary +hmat.value = np.minimum(hmat.value, Smax) + +hmat.slope = np.full( hmat.value.shape, +0.1500 , dtype=jigsawpy.jigsaw_msh_t.REALS_t) + +#------------------------------------ do stereographic proj. +geom.point["coord"][:, :] *= np.pi / 180. + +proj.prjID = 'stereographic' +proj.radii = +6.371E+003 +proj.xbase = +0.500 * (xmin + xmax) * np.pi / 180. +proj.ybase = +0.500 * (ymin + ymax) * np.pi / 180. + +jigsawpy.savemsh(OutDir+"HFUN.msh", hmat) + +jigsawpy.project(geom, proj, "fwd") +jigsawpy.project(hmat, proj, "fwd") + +jigsawpy.savemsh(opts.geom_file, geom) +jigsawpy.savemsh(opts.hfun_file, hmat) + +# save hmat------------------------------------------------------------ +jigsawpy.savemsh(OutDir+"HFUNproj0.msh", hmat) + +#smooth hmat +jigsawpy.cmd.marche(opts, hmat) + +# save smoothed hmat--------------------------------------------------- +jigsawpy.savemsh(OutDir+"HFUNproj1.msh", hmat) + +# make mesh using JIGSAW----------------------------------------------- +opts.hfun_scal = "absolute" +opts.hfun_hmax = float("inf") # null HFUN limits +opts.hfun_hmin = float(+0.00) +#opts.hfun_hmax = 1.25*Smax # Unintended effects, better off null +#opts.hfun_hmin = .5*Smin + +opts.mesh_dims = +2 # 2-dim. simplexes +opts.mesh_eps1 = +1. + +#opts.mesh_top1 = "true" !!!No convergece + + +ttic = time.time() + +jigsawpy.cmd.jigsaw(opts, mesh) + +ttoc = time.time() + +print("CPUSEC =", (ttoc - ttic)) + +# save mesh in JIGSAW native projection based on input PSLG------------ +jigsawpy.savemsh(OutDir+"/RWPS.PROJ.msh",mesh) + +# compute costa functions and save +cost = jigsawpy.triscr2(mesh.point["coord"],mesh.tria3["index"]) +np.savetxt(OutDir+"TriScr2.txt",cost,"%f") +print("TRISCR =", np.min(cost), np.mean(cost)) + +cost = jigsawpy.pwrscr2(mesh.point["coord"], mesh.power, mesh.tria3["index"]) +np.savetxt(OutDir+"PwrScr2.txt",cost,"%f") +print("PWRSCR =", np.min(cost), np.mean(cost)) + +tbad = jigsawpy.centre2(mesh.point["coord"],mesh.power,mesh.tria3["index"]) +print("OBTUSE =",+np.count_nonzero(np.logical_not(tbad))) + + +# project mesh nodes to radian lat, lon +jigsawpy.project(mesh, proj, "inv") # This used to work +#jigsawpy.savemsh("RWPS.radian.msh",mesh) + +# transform mesh nodes to degree lat, lon +mesh.point["coord"][:, :] = mesh.point["coord"][:, :]*180. / np.pi + +# save mesh in degree lat, lon system +jigsawpy.savemsh(OutDir+"RWPS.LL.msh",mesh) + +# create jigsaw R3 mesh on global surface and save to +S2=mesh.point["coord"][:,[0,1]] +S2=S2*np.pi/180. + +R3=jigsawpy.S2toR3(mesh.radii,S2) +#np.savetxt("R3.txt",R3," %f ") # save 3D nodes + +meshR3 = jigsawpy.jigsaw_msh_t() +mesh.mshID = 'ellipsoid-mesh' +meshR3.tria3=mesh.tria3 +meshR3.ndims=3 +#make 3D coordinates +nd=R3.shape +meshR3.vert3 = np.zeros(nd[0], dtype=mesh.VERT3_t) +meshR3.vert3["coord"] = R3 +jigsawpy.savemsh(OutDir+"RWPS.R3.msh",meshR3) + + +# Now apply filters and output in WW3 form +# import FilterRoutines.py + +from FilterRoutinesNM import * + +#replace mesh with R3 mesh, mesh->mesh R3 +jigsawpy.loadmsh(OutDir+"RWPS.R3.msh", mesh) # uncomment if starting here + +opts.geom_file = "geom.msh" #saves the geometry info for jigsaw +opts.jcfg_file = "opts.jig" #jigsaw ctlr file + + +geom.mshID = "ellipsoid-mesh" +geom.radii = np.full(3, 6.371E+003, dtype=geom.REALS_t) + +jigsawpy.savemsh(opts.geom_file, geom) + +inject_dem() +filter_ocn() + +jigsawpy.savemsh(OutDir+"RWPS.F.R3.msh", mesh) + +# viz. in eg. paraview +jigsawpy.savevtk(OutDir+"test.vtk", mesh) + +# convert to lon lat +point = mesh.point["coord"] +point = jigsawpy.R3toS2(geom.radii, point) +point*= 180. / np.pi + +depth = np.reshape(-1*mesh.value, (mesh.value.size, 1)) +depth[depth <= 0] = 2 +point = np.hstack((point, depth)) # append elev. as 3rd coord. +cells = [("triangle", mesh.tria3["index"])] +tri_data=cells[0][1]+1 + +#put coordinates in non standard format to avoid international date line +lon=point[:,0] +lon[np.where(lon>90)]=lon[np.where(lon>90)]-360 +point[:,0]=lon + +write_gmsh_mesh(OutDir+"RWPS.ww3", point, tri_data) + +mesh.point["coord"]=point +jigsawpy.savemsh(OutDir+"RWPS.F.LLH.msh", mesh) + + +#write final mesh in jigsaw .msh format +meshR2 = jigsawpy.jigsaw_msh_t() +#make 2D coordinates +nd=point.shape +meshR2.ndims=2 +meshR2.vert2 = np.zeros(nd[0], dtype=mesh.VERT2_t) +meshR2.vert2["coord"] = point[:,[0,1]] +meshR2.tria3=mesh.tria3 +meshR2.mshID=mesh.mshID + +jigsawpy.savemsh(OutDir+"RWPS.F.LL.msh", meshR2) + diff --git a/unst_msh_gen/regional/MeshGenTemplateDirectory/RWPSMeshGenScript.OSMxGSSHS.NewOrl.py b/unst_msh_gen/regional/MeshGenTemplateDirectory/RWPSMeshGenScript.OSMxGSSHS.NewOrl.py new file mode 100644 index 0000000..db08dc2 --- /dev/null +++ b/unst_msh_gen/regional/MeshGenTemplateDirectory/RWPSMeshGenScript.OSMxGSSHS.NewOrl.py @@ -0,0 +1,265 @@ + +import os +import argparse +import time +import numpy as np +import netCDF4 as nc + +import jigsawpy + +from scipy.interpolate import RegularGridInterpolator + +#-------------------Input Files---------------------------------------- + +""" +#OSM coastline +PSLGFile="GlobalCoastlineOSM.PSLG.msh" +DistanceToCoastFile="DFun.GlobalCoastlineOSM.PSLG.msh" +TopographyFile="Topo.DFun.GlobalCoastlineOSM.PSLG.msh" + +#GSSH coastline +#jigsaw .msh format Planer Straight Line Graph defining mesh outer boundary and coastline +PSLGFile="GlobalCoastlineGSHHS.PSLG.msh" +#jigsaw gridded .msh format Distance to taget poings +DistanceToCoastFile="DFun.GlobalCoastlineGSHHS.PSLG.msh" +#jigsaw gridded .msh format topography on same grid as distance +TopographyFile="DFun.GlobalCoastlineGSHHS.PSLG.msh" +""" + +# GSHHS except identified problem spots (i.e. American Samoa) where OSM is used +#jigsaw .msh format Planer Straight Line Graph defining mesh outer boundary and coastline +PSLGFile="/scratch3/NCEPDEV/climate/Keston.Smith/RWPS/Data/JigsawFormatFiles/PSLGboundaryOSMxGSHHS1kmBOXES.msh" +#jigsaw gridded .msh format Distance to taget poings +DistanceToCoastFile="/scratch3/NCEPDEV/climate/Keston.Smith/RWPS/Data/JigsawFormatFiles/DistToUScoast_shifted.OSMxGSHHS.Boxes.msh" +#jigsaw gridded .msh format topography on same grid as distance +TopographyFile="/scratch3/NCEPDEV/climate/Keston.Smith/RWPS/Data/JigsawFormatFiles/Topo_shifted.OSMxGSHHS.Boxes.msh" + +# directory to write output files to +OutDir='RWPS.OSMxGSHHS.NewOrl/' + +ww3_mesh_file='RWPS.GSHHSxGSHHS.NewOrl.WW3.msh' + +#-------------------Paramter Inputs------------------------------------ +#parameters for specifying resolution +d0=10000. +d1=320000. - d0 +beta=1000. +Smin=0.5 +Smax=10. + +#-------------------Main Program--------------------------------------- + +# Create the output directory------------------------------------------ +try: + os.mkdir(OutDir) + print(f"Directory '{OutDir}' created successfully.") +except FileExistsError: + print(f"Directory '{OutDir}' already exists. Proceeding ...") +except PermissionError: + print(f"Permission denied: Unable to create '{OutDir}'.") + +# Setup jigsaw structures---------------------------------------------- +opts = jigsawpy.jigsaw_jig_t() +topo = jigsawpy.jigsaw_msh_t() #topographic database +dist = jigsawpy.jigsaw_msh_t() #distance to US coastline in meters +geom = jigsawpy.jigsaw_msh_t() +mesh = jigsawpy.jigsaw_msh_t() +meshR3 = jigsawpy.jigsaw_msh_t() +hmat = jigsawpy.jigsaw_msh_t() +proj = jigsawpy.jigsaw_prj_t() + +opts.geom_file = "geom.msh" +opts.jcfg_file = "jcfg.jig" +opts.mesh_file = "mesh.msh" +opts.hfun_file = "spac.msh" + +# load input data------------------------------------------------------ +jigsawpy.loadmsh(PSLGFile, geom) +jigsawpy.loadmsh(DistanceToCoastFile, dist) +jigsawpy.loadmsh(TopographyFile, topo) + +# truncate data to bounding rectangle of input PSLG-------------------- +xmin = np.min( geom.point["coord"][:, 0]) +ymin = np.min( geom.point["coord"][:, 1]) +xmax = np.max( geom.point["coord"][:, 0]) +ymax = np.max( geom.point["coord"][:, 1]) + +tv = topo.value +dv = dist.value + +xmsk = np.logical_and( topo.xgrid > xmin , topo.xgrid < xmax ) +ymsk = np.logical_and( topo.ygrid > ymin , topo.ygrid < ymax ) + +tv = tv[:, xmsk] +tv = tv[ymsk, :] +dv = dv[:, xmsk] +dv = dv[ymsk, :] + +# define spatial resolution shape function----------------------------- +W=np.exp(- (np.abs(dv-d0) / d1) - (np.abs(tv) / beta) ) +W[ np.where(dv < d0 ) ] = 1. + +# build resolution specification gridded data structure, hmat---------- +hmat=topo +hmat.value=Smax - (Smax - Smin)*W + +hmat.mshID = "ellipsoid-grid" +hmat.radii = np.full( +3, +6371.0, dtype=jigsawpy.jigsaw_msh_t.REALS_t) + +hmat.xgrid = hmat.xgrid[xmsk] * np.pi / 180. +hmat.ygrid = hmat.ygrid[ymsk] * np.pi / 180. + +hmat.value = np.maximum(hmat.value, Smin)#should not be nescesary +hmat.value = np.minimum(hmat.value, Smax) + +hmat.slope = np.full( hmat.value.shape, +0.1500 , dtype=jigsawpy.jigsaw_msh_t.REALS_t) + +#------------------------------------ do stereographic proj. +geom.point["coord"][:, :] *= np.pi / 180. + +proj.prjID = 'stereographic' +proj.radii = +6.371E+003 +proj.xbase = +0.500 * (xmin + xmax) * np.pi / 180. +proj.ybase = +0.500 * (ymin + ymax) * np.pi / 180. + +jigsawpy.savemsh(OutDir+"HFUN.msh", hmat) + +jigsawpy.project(geom, proj, "fwd") +jigsawpy.project(hmat, proj, "fwd") + +jigsawpy.savemsh(opts.geom_file, geom) +jigsawpy.savemsh(opts.hfun_file, hmat) + +# save hmat------------------------------------------------------------ +jigsawpy.savemsh(OutDir+"HFUNproj0.msh", hmat) + +#smooth hmat +jigsawpy.cmd.marche(opts, hmat) + +# save smoothed hmat--------------------------------------------------- +jigsawpy.savemsh(OutDir+"HFUNproj1.msh", hmat) + +# make mesh using JIGSAW----------------------------------------------- +opts.hfun_scal = "absolute" +opts.hfun_hmax = float("inf") # null HFUN limits +opts.hfun_hmin = float(+0.00) +#opts.hfun_hmax = 1.25*Smax # Unintended effects, better off null +#opts.hfun_hmin = .5*Smin + +opts.mesh_dims = +2 # 2-dim. simplexes +opts.mesh_eps1 = +1. + +#opts.mesh_top1 = "true" !!!No convergece + + +ttic = time.time() + +jigsawpy.cmd.jigsaw(opts, mesh) + +ttoc = time.time() + +print("CPUSEC =", (ttoc - ttic)) + +# save mesh in JIGSAW native projection based on input PSLG------------ +jigsawpy.savemsh(OutDir+"/RWPS.PROJ.msh",mesh) + +# compute costa functions and save +cost = jigsawpy.triscr2(mesh.point["coord"],mesh.tria3["index"]) +np.savetxt(OutDir+"TriScr2.txt",cost,"%f") +print("TRISCR =", np.min(cost), np.mean(cost)) + +cost = jigsawpy.pwrscr2(mesh.point["coord"], mesh.power, mesh.tria3["index"]) +np.savetxt(OutDir+"PwrScr2.txt",cost,"%f") +print("PWRSCR =", np.min(cost), np.mean(cost)) + +tbad = jigsawpy.centre2(mesh.point["coord"],mesh.power,mesh.tria3["index"]) +print("OBTUSE =",+np.count_nonzero(np.logical_not(tbad))) + + +# project mesh nodes to radian lat, lon +jigsawpy.project(mesh, proj, "inv") # This used to work +#jigsawpy.savemsh("RWPS.radian.msh",mesh) + +# transform mesh nodes to degree lat, lon +mesh.point["coord"][:, :] = mesh.point["coord"][:, :]*180. / np.pi + +# save mesh in degree lat, lon system +jigsawpy.savemsh(OutDir+"RWPS.LL.msh",mesh) + +# create jigsaw R3 mesh on global surface and save to +S2=mesh.point["coord"][:,[0,1]] +S2=S2*np.pi/180. + +R3=jigsawpy.S2toR3(mesh.radii,S2) +#np.savetxt("R3.txt",R3," %f ") # save 3D nodes + +meshR3 = jigsawpy.jigsaw_msh_t() +mesh.mshID = 'ellipsoid-mesh' +meshR3.tria3=mesh.tria3 +meshR3.ndims=3 +#make 3D coordinates +nd=R3.shape +meshR3.vert3 = np.zeros(nd[0], dtype=mesh.VERT3_t) +meshR3.vert3["coord"] = R3 +jigsawpy.savemsh(OutDir+"RWPS.R3.msh",meshR3) + + +# Now apply filters and output in WW3 form +# import FilterRoutines.py + +from FilterRoutinesNM import * + +#replace mesh with R3 mesh, mesh->mesh R3 +jigsawpy.loadmsh(OutDir+"RWPS.R3.msh", mesh) # uncomment if starting here + +opts.geom_file = "geom.msh" #saves the geometry info for jigsaw +opts.jcfg_file = "opts.jig" #jigsaw ctlr file + + +geom.mshID = "ellipsoid-mesh" +geom.radii = np.full(3, 6.371E+003, dtype=geom.REALS_t) + +jigsawpy.savemsh(opts.geom_file, geom) + +inject_dem() +filter_ocn() + +jigsawpy.savemsh(OutDir+"RWPS.F.R3.msh", mesh) + +# viz. in eg. paraview +jigsawpy.savevtk(OutDir+"test.vtk", mesh) + +# convert to lon lat +point = mesh.point["coord"] +point = jigsawpy.R3toS2(geom.radii, point) +point*= 180. / np.pi + +depth = np.reshape(-1*mesh.value, (mesh.value.size, 1)) +depth[depth <= 0] = 2 +point = np.hstack((point, depth)) # append elev. as 3rd coord. +cells = [("triangle", mesh.tria3["index"])] +tri_data=cells[0][1]+1 + +#put coordinates in non standard format to avoid international date line +lon=point[:,0] +lon[np.where(lon>90)]=lon[np.where(lon>90)]-360 +point[:,0]=lon + +write_gmsh_mesh(OutDir+"RWPS.ww3", point, tri_data) + +mesh.point["coord"]=point +jigsawpy.savemsh(OutDir+"RWPS.F.LLH.msh", mesh) + + +#write final mesh in jigsaw .msh format +meshR2 = jigsawpy.jigsaw_msh_t() +#make 2D coordinates +nd=point.shape +meshR2.ndims=2 +meshR2.vert2 = np.zeros(nd[0], dtype=mesh.VERT2_t) +meshR2.vert2["coord"] = point[:,[0,1]] +meshR2.tria3=mesh.tria3 +meshR2.mshID=mesh.mshID + +jigsawpy.savemsh(OutDir+"RWPS.F.LL.msh", meshR2) + diff --git a/unst_msh_gen/regional/MeshGenTemplateDirectory/RWPSMeshGenScript.py b/unst_msh_gen/regional/MeshGenTemplateDirectory/RWPSMeshGenScript.py new file mode 100644 index 0000000..4dadb86 --- /dev/null +++ b/unst_msh_gen/regional/MeshGenTemplateDirectory/RWPSMeshGenScript.py @@ -0,0 +1,267 @@ + +import os +import argparse +import time +import numpy as np +import netCDF4 as nc + +import jigsawpy + +from scipy.interpolate import RegularGridInterpolator + +#-------------------Input Files---------------------------------------- + +""" +#OSM coastline +PSLGFile="GlobalCoastlineOSM.PSLG.msh" +DistanceToCoastFile="DFun.GlobalCoastlineOSM.PSLG.msh" +TopographyFile="Topo.DFun.GlobalCoastlineOSM.PSLG.msh" +""" + +#GSSH coastline +#jigsaw .msh format Planer Straight Line Graph defining mesh outer boundary and coastline +PSLGFile="GlobalCoastlineGSHHS.PSLG.msh" +#jigsaw gridded .msh format Distance to taget poings +DistanceToCoastFile="DFun.GlobalCoastlineGSHHS.PSLG.msh" +#jigsaw gridded .msh format topography on same grid as distance +TopographyFile="DFun.GlobalCoastlineGSHHS.PSLG.msh" + +""" +# GSHHS except identified problem spots (i.e. American Samoa) where OSM is used +#jigsaw .msh format Planer Straight Line Graph defining mesh outer boundary and coastline +PSLGFile="PSLGboundaryOSMxGSHHS.BOXES.msh" +#jigsaw gridded .msh format Distance to taget poings +DistanceToCoastFile="DFun.PSLGboundaryOSMxGSHHS.BOXES.msh" +#jigsaw gridded .msh format topography on same grid as distance +TopographyFile="Topo.DFun.PSLGboundaryOSMxGSHHS.BOXES.msh" +""" + +# directory to write output files to +OutDir='testGSHHS/' + +ww3_mesh_file='RWPS.GSHHS.WW3.msh' + +#-------------------Paramter Inputs------------------------------------ +#parameters for specifying resolution +d0=10000. +d1=320000. - d0 +beta=1000. +Smin=0.5 +Smax=10. + +#-------------------Main Program--------------------------------------- + +# Create the output directory------------------------------------------ +try: + os.mkdir(OutDir) + print(f"Directory '{OutDir}' created successfully.") +except FileExistsError: + print(f"Directory '{OutDir}' already exists. Proceeding ...") +except PermissionError: + print(f"Permission denied: Unable to create '{OutDir}'.") + +# Setup jigsaw structures---------------------------------------------- +opts = jigsawpy.jigsaw_jig_t() +topo = jigsawpy.jigsaw_msh_t() #topographic database +dist = jigsawpy.jigsaw_msh_t() #distance to US coastline in meters +geom = jigsawpy.jigsaw_msh_t() +mesh = jigsawpy.jigsaw_msh_t() +meshR3 = jigsawpy.jigsaw_msh_t() +hmat = jigsawpy.jigsaw_msh_t() +proj = jigsawpy.jigsaw_prj_t() + +opts.geom_file = "geom.msh" +opts.jcfg_file = "jcfg.jig" +opts.mesh_file = "mesh.msh" +opts.hfun_file = "spac.msh" + +# load input data------------------------------------------------------ +jigsawpy.loadmsh(PSLGFile, geom) +jigsawpy.loadmsh(DistanceToCoastFile, dist) +jigsawpy.loadmsh(TopographyFile, topo) + +# truncate data to bounding rectangle of input PSLG-------------------- +xmin = np.min( geom.point["coord"][:, 0]) +ymin = np.min( geom.point["coord"][:, 1]) +xmax = np.max( geom.point["coord"][:, 0]) +ymax = np.max( geom.point["coord"][:, 1]) + +tv = topo.value +dv = dist.value + +xmsk = np.logical_and( topo.xgrid > xmin , topo.xgrid < xmax ) +ymsk = np.logical_and( topo.ygrid > ymin , topo.ygrid < ymax ) + +tv = tv[:, xmsk] +tv = tv[ymsk, :] +dv = dv[:, xmsk] +dv = dv[ymsk, :] + +# define spatial resolution shape function----------------------------- +W=np.exp(- (np.abs(dv-d0) / d1) - (np.abs(tv) / beta) ) +W[ np.where(dv < d0 ) ] = 1. + +# build resolution specification gridded data structure, hmat---------- +hmat=topo +hmat.value=Smax - (Smax - Smin)*W + +hmat.mshID = "ellipsoid-grid" +hmat.radii = np.full( +3, +6371.0, dtype=jigsawpy.jigsaw_msh_t.REALS_t) + +hmat.xgrid = hmat.xgrid[xmsk] * np.pi / 180. +hmat.ygrid = hmat.ygrid[ymsk] * np.pi / 180. + +hmat.value = np.maximum(hmat.value, Smin)#should not be nescesary +hmat.value = np.minimum(hmat.value, Smax) + +hmat.slope = np.full( hmat.value.shape, +0.1500 , dtype=jigsawpy.jigsaw_msh_t.REALS_t) + +#------------------------------------ do stereographic proj. +geom.point["coord"][:, :] *= np.pi / 180. + +proj.prjID = 'stereographic' +proj.radii = +6.371E+003 +proj.xbase = +0.500 * (xmin + xmax) * np.pi / 180. +proj.ybase = +0.500 * (ymin + ymax) * np.pi / 180. + +jigsawpy.savemsh(OutDir+"HFUN.msh", hmat) + +jigsawpy.project(geom, proj, "fwd") +jigsawpy.project(hmat, proj, "fwd") + +jigsawpy.savemsh(opts.geom_file, geom) +jigsawpy.savemsh(opts.hfun_file, hmat) + +# save hmat------------------------------------------------------------ +jigsawpy.savemsh(OutDir+"HFUNproj0.msh", hmat) + +#smooth hmat +jigsawpy.cmd.marche(opts, hmat) + +# save smoothed hmat--------------------------------------------------- +jigsawpy.savemsh(OutDir+"HFUNproj1.msh", hmat) + +# make mesh using JIGSAW----------------------------------------------- +opts.hfun_scal = "absolute" +opts.hfun_hmax = float("inf") # null HFUN limits +opts.hfun_hmin = float(+0.00) +#opts.hfun_hmax = 1.25*Smax # Unintended effects, better off null +#opts.hfun_hmin = .5*Smin + +opts.mesh_dims = +2 # 2-dim. simplexes +opts.mesh_eps1 = +1. + +#opts.mesh_top1 = "true" !!!No convergece + + +ttic = time.time() + +jigsawpy.cmd.jigsaw(opts, mesh) + +ttoc = time.time() + +print("CPUSEC =", (ttoc - ttic)) + +# save mesh in JIGSAW native projection based on input PSLG------------ +jigsawpy.savemsh(OutDir+"/RWPS.PROJ.msh",mesh) + +# compute costa functions and save +cost = jigsawpy.triscr2(mesh.point["coord"],mesh.tria3["index"]) +np.savetxt(OutDir+"TriScr2.txt",cost,"%f") +print("TRISCR =", np.min(cost), np.mean(cost)) + +cost = jigsawpy.pwrscr2(mesh.point["coord"], mesh.power, mesh.tria3["index"]) +np.savetxt(OutDir+"PwrScr2.txt",cost,"%f") +print("PWRSCR =", np.min(cost), np.mean(cost)) + +tbad = jigsawpy.centre2(mesh.point["coord"],mesh.power,mesh.tria3["index"]) +print("OBTUSE =",+np.count_nonzero(np.logical_not(tbad))) + + +# project mesh nodes to radian lat, lon +jigsawpy.project(mesh, proj, "inv") # This used to work +#jigsawpy.savemsh("RWPS.radian.msh",mesh) + +# transform mesh nodes to degree lat, lon +mesh.point["coord"][:, :] = mesh.point["coord"][:, :]*180. / np.pi + +# save mesh in degree lat, lon system +jigsawpy.savemsh(OutDir+"RWPS.LL.msh",mesh) + +# create jigsaw R3 mesh on global surface and save to +S2=mesh.point["coord"][:,[0,1]] +S2=S2*np.pi/180. + +R3=jigsawpy.S2toR3(mesh.radii,S2) +#np.savetxt("R3.txt",R3," %f ") # save 3D nodes + +meshR3 = jigsawpy.jigsaw_msh_t() +mesh.mshID = 'ellipsoid-mesh' +meshR3.tria3=mesh.tria3 +meshR3.ndims=3 +#make 3D coordinates +nd=R3.shape +meshR3.vert3 = np.zeros(nd[0], dtype=mesh.VERT3_t) +meshR3.vert3["coord"] = R3 +jigsawpy.savemsh(OutDir+"RWPS.R3.msh",meshR3) + + +# Now apply filters and output in WW3 form +# import FilterRoutines.py + +from FilterRoutinesNM import * + +#replace mesh with R3 mesh, mesh->mesh R3 +jigsawpy.loadmsh(OutDir+"RWPS.R3.msh", mesh) # uncomment if starting here + +opts.geom_file = "geom.msh" #saves the geometry info for jigsaw +opts.jcfg_file = "opts.jig" #jigsaw ctlr file + + +geom.mshID = "ellipsoid-mesh" +geom.radii = np.full(3, 6.371E+003, dtype=geom.REALS_t) + +jigsawpy.savemsh(opts.geom_file, geom) + +inject_dem() +filter_ocn() + +jigsawpy.savemsh(OutDir+"RWPS.F.R3.msh", mesh) + +# viz. in eg. paraview +jigsawpy.savevtk(OutDir+"test.vtk", mesh) + +# convert to lon lat +point = mesh.point["coord"] +point = jigsawpy.R3toS2(geom.radii, point) +point*= 180. / np.pi + +depth = np.reshape(-1*mesh.value, (mesh.value.size, 1)) +depth[depth <= 0] = 2 +point = np.hstack((point, depth)) # append elev. as 3rd coord. +cells = [("triangle", mesh.tria3["index"])] +tri_data=cells[0][1]+1 + +#put coordinates in non standard format to avoid international date line +lon=point[:,0] +lon[np.where(lon>90)]=lon[np.where(lon>90)]-360 +point[:,0]=lon + +write_gmsh_mesh(OutDir+"RWPS.ww3", point, tri_data) + +mesh.point["coord"]=point +jigsawpy.savemsh(OutDir+"RWPS.F.LLH.msh", mesh) + + +#write final mesh in jigsaw .msh format +meshR2 = jigsawpy.jigsaw_msh_t() +#make 2D coordinates +nd=point.shape +meshR2.ndims=2 +meshR2.vert2 = np.zeros(nd[0], dtype=mesh.VERT2_t) +meshR2.vert2["coord"] = point[:,[0,1]] +meshR2.tria3=mesh.tria3 +meshR2.mshID=mesh.mshID + +jigsawpy.savemsh(OutDir+"RWPS.F.LL.msh", meshR2) + diff --git a/unst_msh_gen/regional/MeshGenTemplateDirectory/SetPath.m b/unst_msh_gen/regional/MeshGenTemplateDirectory/SetPath.m new file mode 100644 index 0000000..177c17f --- /dev/null +++ b/unst_msh_gen/regional/MeshGenTemplateDirectory/SetPath.m @@ -0,0 +1,31 @@ + +addpath ../matlab +addpath ../matlab/jigsaw-matlab +addpath ../matlab/inpoly + + +% set global variables defining paths to different data sets +global GlobalTopoFile uscl gcfl gcflGSHHS gcflOSM PSLGfile + +% Global coverage "low" resolution bathymetry file +GlobalTopoFile='../Data/Bathymetry/RTopo_2_0_4_GEBCO_v2024_60sec_pixel.nc' + +% US coastline file or shapefile defining coastal points for high resolution +uscl='../Data/us_coastline/tl_2023_us_coastline.shp' + +%Global coverage coastline file from () +gcflGSHHS='../Data/GSHHS_shp/f/GSHHS_f_L1.shp' + +%Global coverage coastline file from OpenStreetMap +gcflOSM='../Data/openstreetmap_land/land-polygons-complete-4326/land_polygons.shp' + +%which Global coverage coastline file is to be used +gcfl=gcflGSHHS + +%Output file from BuildBoundaryPSLGwGSHHS and input to MakeDfunGSHHS +PSLGfile='GlobalCoastlineGSHHS.PSLG.msh' + +%For NWcoastal mesh uncomment the following lines +%gcfl=gcflOSM +%PSLGfile='NWcoastal.PSLG.msh' + diff --git a/unst_msh_gen/regional/MeshGenTemplateDirectory/jigsaw.ursa.NewOrl.job b/unst_msh_gen/regional/MeshGenTemplateDirectory/jigsaw.ursa.NewOrl.job new file mode 100644 index 0000000..7c94ed0 --- /dev/null +++ b/unst_msh_gen/regional/MeshGenTemplateDirectory/jigsaw.ursa.NewOrl.job @@ -0,0 +1,32 @@ +#!/bin/bash +#SBATCH --job-name=jigsaw_py_RWPSng # Job name +#SBATCH --mail-type=END # Mail events (BEGIN, END, FAIL, ALL) +#SBATCH --mail-user=keston.smith@noaa.gov # Where to send mail +##SBATCH --ntasks=144 # Number of MPI ranks +#SBATCH --mem-per-cpu=256000mb # Memory per processor +#SBATCH --time=08:00:00 # Time limit hrs:min:sec +#SBATCH --output=mpi_test_%j.log # Standard output/error +#SBATCH --exclusive=user +#SBATCH --error=%j.err +##SBATCH --partition=marine-cpu +#SBATCH --account=marine-cpu +#SBATCH --nodes=1 +#SBATCH --ntasks=1 + + + +module purge +module use /scratch3/NCEPDEV/climate/Keston.Smith/global-workflowA/sorc/ufs_model.fd/modulefiles/ +module load ufs_ursa.intel +module load stack-oneapi/2024.2.1 +module load python/3.11 +module load cmake/3.27.9 + +module load py-numpy/1.26.4 +module load ufs_ursa.intel +module load py-netcdf4/1.7.1.post2 + +source /scratch3/NCEPDEV/climate/Keston.Smith/UrsaMeshgenEnv/jigsawenv/bin/activate +pip list -v + +python3 RWPSMeshGenScript.OSMxGSSHS.NewOrl.py > NonGlobalMeshGen.OSMxGSSHS.NewOrl.out.txt diff --git a/unst_msh_gen/regional/MeshGenTemplateDirectory/jigsaw.ursa.job b/unst_msh_gen/regional/MeshGenTemplateDirectory/jigsaw.ursa.job new file mode 100644 index 0000000..29b97c1 --- /dev/null +++ b/unst_msh_gen/regional/MeshGenTemplateDirectory/jigsaw.ursa.job @@ -0,0 +1,33 @@ +#!/bin/bash +#SBATCH --job-name=jigsaw_py_RWPSng # Job name +#SBATCH --mail-type=END # Mail events (BEGIN, END, FAIL, ALL) +#SBATCH --mail-user=keston.smith@noaa.gov # Where to send mail +##SBATCH --ntasks=144 # Number of MPI ranks +#SBATCH --mem-per-cpu=256000mb # Memory per processor +#SBATCH --time=08:00:00 # Time limit hrs:min:sec +#SBATCH --output=mpi_test_%j.log # Standard output/error +##SBATCH --exclusive=user +#SBATCH --error=%j.err +##SBATCH --partition=marine-cpu +#SBATCH --account=marine-cpu +#SBATCH --nodes=1 +#SBATCH --ntasks=1 + + + +module purge +module use /scratch3/NCEPDEV/climate/Keston.Smith/global-workflowA/sorc/ufs_model.fd/modulefiles/ +module load ufs_ursa.intel +module load stack-oneapi/2024.2.1 +module load python/3.11 +module load cmake/3.27.9 + +module load py-numpy/1.26.4 +module load ufs_ursa.intel +module load py-netcdf4/1.7.1.post2 + +source /scratch3/NCEPDEV/climate/Keston.Smith/UrsaMeshgenEnv/jigsawenv/bin/activate +pip list -v + +##python3 RWPSMeshGenScript.py > NonGlobalMeshGen.out.txt +python3 RWPSMeshGenScript.GSHHS.py > RWPSMeshGenScript.GSHHS.out diff --git a/unst_msh_gen/regional/MeshGenTemplateDirectory/jobcard.matlab.postjigsaw b/unst_msh_gen/regional/MeshGenTemplateDirectory/jobcard.matlab.postjigsaw new file mode 100644 index 0000000..5499005 --- /dev/null +++ b/unst_msh_gen/regional/MeshGenTemplateDirectory/jobcard.matlab.postjigsaw @@ -0,0 +1,16 @@ +#!/bin/sh +#SBATCH --nodes=1 +#SBATCH --ntasks=32 # Number of tasks/cores +#SBATCH --ntasks-per-node=32 +#SBATCH -q batch +#SBATCH -t 08:00:00 +#SBATCH -A marine-cpu +#SBATCH -J MeshPostProcess +#SBATCH -o MeshPostProcess.out + + +# Load the MATLAB module +module load matlab/R2024b # Specify the version you need + +# Run your MATLAB script without a graphical interface +matlab -nodisplay -nodesktop -r "run('./PostProcessGrid.m'); exit;" diff --git a/unst_msh_gen/regional/MeshGenTemplateDirectory/jobcard.matlab.postjigsaw.OSMxGSHHS.NewOrl b/unst_msh_gen/regional/MeshGenTemplateDirectory/jobcard.matlab.postjigsaw.OSMxGSHHS.NewOrl new file mode 100644 index 0000000..f110bbd --- /dev/null +++ b/unst_msh_gen/regional/MeshGenTemplateDirectory/jobcard.matlab.postjigsaw.OSMxGSHHS.NewOrl @@ -0,0 +1,16 @@ +#!/bin/sh +#SBATCH --nodes=1 +#SBATCH --ntasks=32 # Number of tasks/cores +#SBATCH --ntasks-per-node=32 +#SBATCH -q batch +#SBATCH -t 08:00:00 +#SBATCH -A marine-cpu +#SBATCH -J MeshPostProcess +#SBATCH -o MeshPostProcess.out + + +# Load the MATLAB module +module load matlab/R2024b # Specify the version you need + +# Run your MATLAB script without a graphical interface +matlab -nodisplay -nodesktop -r "run('./PostProcessGrid_OSMxGSHHS_NewOrl.m'); exit;" diff --git a/unst_msh_gen/regional/MeshGenTemplateDirectory/jobcard.matlab.prejigsaw b/unst_msh_gen/regional/MeshGenTemplateDirectory/jobcard.matlab.prejigsaw new file mode 100644 index 0000000..9227942 --- /dev/null +++ b/unst_msh_gen/regional/MeshGenTemplateDirectory/jobcard.matlab.prejigsaw @@ -0,0 +1,16 @@ +#!/bin/sh +#SBATCH --nodes=1 +#SBATCH --ntasks=32 # Number of tasks/cores +#SBATCH --ntasks-per-node=32 +#SBATCH -q batch +#SBATCH -t 08:00:00 +#SBATCH -A marine-cpu +#SBATCH -J MeshPreProcess +#SBATCH -o MeshPreProcess.out + + +# Load the MATLAB module +module load matlab/R2024b # Specify the version you need + +# Run your MATLAB script without a graphical interface +matlab -nodisplay -nodesktop -r "run('./PreProcessCoastline.m'); exit;" diff --git a/unst_msh_gen/regional/README.txt b/unst_msh_gen/regional/README.txt new file mode 100644 index 0000000..d6b55dc --- /dev/null +++ b/unst_msh_gen/regional/README.txt @@ -0,0 +1,5 @@ +Documentation currently in slideshow format at: + +https://docs.google.com/presentation/d/1P7M8eI6AkELXyCL1E4VQZSmJUk0EUZ344zdyeifffn8 + +Text documentation will be added later. diff --git a/unst_msh_gen/regional/matlab/BoundaryShape2msh.m b/unst_msh_gen/regional/matlab/BoundaryShape2msh.m new file mode 100644 index 0000000..d82f132 --- /dev/null +++ b/unst_msh_gen/regional/matlab/BoundaryShape2msh.m @@ -0,0 +1,57 @@ +function geom=BoundaryShape2msh(S,flout) +% function geom=BoundaryShape2msh(S,flout) +% make coastline read from a shape file into a jigsaw format mesh file. +% inputs: +% S: shapefile structure, i.e. S=shaperead('us_coastline.shp'); +% flout: file to write jigsaw format .msh file representing S +% outputs: +% geom : jigsaw structure representing S +% + +clear geom +geom.mshID='EUCLIDEAN-MESH' +geom.fileV = 3 +if isstr(S) + S = shaperead(S); +end + +N=length(S); +x0=[]; +y0=[]; +edge0=[]; +for k=1:N + if mod(k,100)==0,k/N,end + x=S(k).X(1:end-1); + y=S(k).Y(1:end-1); + if ~isempty(x) + isisland=0; + if and( x(1)==x(end),y(1)==y(end) ) + isisland=1; + end + if length(x)>1, + if ~isisland, + n0=length(x0); + x0=[x0;x(:)]; + y0=[y0;y(:)]; + n1=length(x0); + edge0=[edge0;[ n0+1:n1-1;n0+2:n1]' ]; + else + n0=length(x0); + x=x(1:end-1); + y=y(1:end-1); + x0=[x0;x(:)]; + y0=[y0;y(:)]; + n1=length(x0); + edge0=[edge0;[ n0+1:n1-1;n0+2:n1]';[n1,n0+1]]; + end + end + end +end + +[ne,two]=size(edge0); +nn=length(x0); +geom.edge2.index=[edge0,zeros(ne,1)]; +geom.point.coord=[x0(:),y0(:),zeros(nn,1)]; + +savemsh(flout,geom); + diff --git a/unst_msh_gen/regional/matlab/BoundingBox.m b/unst_msh_gen/regional/matlab/BoundingBox.m new file mode 100644 index 0000000..63c301a --- /dev/null +++ b/unst_msh_gen/regional/matlab/BoundingBox.m @@ -0,0 +1,15 @@ +function h=BoundingBox(x,y,c); +%function h=BoundingBox(x,y,c); +% draw a bounding rectanble around points x, y in color c. +% Note, x and y can be different lengths here. + +if nargin<3,c='k';end +x0=min(min(x)); +y0=min(min(y)); +x1=max(max(x)); +y1=max(max(y)); + +hold on; +h=plot([x0,x1,x1,x1,x0,x0],... + [y0,y0,y1,y1,y1,y0],c); + diff --git a/unst_msh_gen/regional/matlab/BoxSmoothTopo.m b/unst_msh_gen/regional/matlab/BoxSmoothTopo.m new file mode 100644 index 0000000..33001fc --- /dev/null +++ b/unst_msh_gen/regional/matlab/BoxSmoothTopo.m @@ -0,0 +1,51 @@ +function topo=BoxSmoothTopo(fl,k) +%function topo=BoxSmoothTopo(fl,k) +% Apply 2D boxcar smoothing and sub sampling of bathymetry in file fl. +% jigsaw can sometimes run into memory problems with large stuctures +% of this type when generating non-global meshes. Retuns topo, a +% gridded jigsaw structure. +% inputs: +% fl : filename pointing to a netcdf bathymetry file with variables +% lon : length (nx+1) longitude of grid +% lat : length (ny+1) latitude of grid +% bed_elevation : ( nx by ny) average bathymetric depth in cell +% k : integer to resample the bathymetry at.The smoothing is carried +% out using a 2 D Boxcar smoother (square average) with sides of +% length (2 k + 1) and resampled every k points +% output: +% topo : jigsaw format girdded data representing the smoothed resampled +% data. +% + + +lon=ncread(fl,'lon'); +lat=ncread(fl,'lat'); +z=ncread(fl,'bed_elevation'); + +lon=[lon(1:end-1)+lon(2:end)]/2; +lat=[lat(1:end-1)+lat(2:end)]/2; +if k==0, + topo.point.coord{:,1}=lon; + topo.point.coord{:,2}=lat;%fix to centers later + topo.value=double(z'); + + topo.mshID='ELLIPSOID-GRID' + topo.fileV=3; + +else + + n=2*k+1; + w=ones(n,n)/n/n; + [nx,ny] = size(z) + zw=conv2(z,w,'same'); + zw=zw(k+1:n:nx-k,k+1:n:ny-k); + lonw=lon(k+1:n:nx-k); + latw=lat(k+1:n:ny-k); + + topo.point.coord{:,1}=lonw; + topo.point.coord{:,2}=latw;%fix to centers later + topo.value=double(zw'); + + topo.mshID='ELLIPSOID-GRID' + topo.fileV=3; +end diff --git a/unst_msh_gen/regional/matlab/BuildBoundaryPSLGfunction.m b/unst_msh_gen/regional/matlab/BuildBoundaryPSLGfunction.m new file mode 100644 index 0000000..cef964a --- /dev/null +++ b/unst_msh_gen/regional/matlab/BuildBoundaryPSLGfunction.m @@ -0,0 +1,424 @@ +function BuildBoundaryPSLGfunctionLC(CoastLineFile,lonWest,lonEast,latSouth,latNorth,FileOutJigsaw) +% BuildBoundaryPSLGfunction(CoastLineFile,lonWest,lonEast,latSouth,latNorth,FileOutJigsaw[optional]) +% Build a boundary Planer Straight Line Graph (PSLG) from a coastline .msh file and +% Bounded oriented lat lon rectangle. +% inputs: +% CoastLineFile : shapefile describing an appropriately smoothed coastline +% for example one created with scripts: MakeCoastalBoundariesGSHHS.m +% in unst_msh_gen/RWPSMeshtoolkit/MeshGenTemplateDirectory/ +% lonWest : bounding west longitude +% lonEast : bounding east longitude +% latSouth : bounding south latitude +% latNorth : bounding north latitude +% FileOutJigsaw : file name to output jigsaw format .msh file +% +% NOTE: If the south west corner (lonWest,latSouth ) is not in the grid (i.e. on land) then the next +% crossing point,moving to the east along the south boundary, should be into +% the intended connected part of the mesh. This has to do with finding and outer boundary in the PSLG +% and could be adjusted near line 271 if needed. +% +% CoastLineFile = 'GlobalCoastlineOSM.shp' +% CoastLineFile = 'GlobalCoastlineGSHHS.shp' +% set:>> lonWest=129.91;lonEast=10.71;latSouth=-30.42;latNorth=79.99; +% or :>> ax=axis;lonWest=ax(1),lonEast=ax(2),latSouth=ax(3),latNorth=ax(4) +% run:>> BuildBoundaryPSLGfunction(CoastLineFile,lonWest,lonEast,latSouth,latNorth) + +% >>west=-127,east=-121,south=45.5,north=50 +% >>BuildBoundaryPSLGfunction('GlobalCoastlineOSM.shp',west,east,south,north) +%Note: One could eliminate the path in the input and use +%SetPath +%CoastLineFile='GlobalCoastline.msh' + +S=shaperead(CoastLineFile) +if nargin<6 + SetPath + FileOutJigsaw=PSLGfile + %FileOutJigsaw=[CoastLineFile(1:end-4),'.PSLG.msh'] +end +FileOutMatlab=[CoastLineFile(1:end-4),'.PSLGtmp.mat'] +isplot=0; + +Blon=[lonWest, lonEast]; +Blat=[latSouth,latNorth]; + +N=length(S); +N1=N; +for k=1:N + if mod(k,1000)==0,k/N,end + lon=S(k).X(1:end-1);%remove trailing nan + lat=S(k).Y(1:end-1); + ns(k)=length(lon); + + lonp=LonCon(lon); + mvp=sum(lon~=lonp);%find how many points change due to LonCon in this segment + if(mvp==ns(k));% if all move then just move boundary + S(k).X=[LonCon(lon),NaN]; + end + if and(mvp>0,mvpminedeges, + ji=find( insidepoly( x,y,Bx,By ) ); + if length(ji)>2 + [xi, yi,ii] = polyxpoly(x, y, Bx, By); + xc=[xc;xi(:)]; + yc=[yc;yi(:)]; + if ~isempty(xi) %modify ob + sxp=[sxp,x(1)];syp=[syp,y(1)]; + [iia,jja]=sort(ii(:,1));%sort to ascending order along segments + xia=xi(jja); + yia=yi(jja); + iis=ii(jja,1);%sorted into ascending order along segments + if ~insidepoly( x(1),y(1),Bx,By )% segment origonates outside box + nseg=length(xia); + for j=1:2:nseg-1, + xs=[xia(j),x( iis(j)+1:iis(j+1) ),xia(j+1)]; + ys=[yia(j),y( iis(j)+1:iis(j+1) ),yia(j+1)]; + dx=abs(xs(2:end)-xs(1:end-1) +i*[ys(2:end)-ys(1:end-1)] ); + n0=length(pslg.x); + pslg.x=[pslg.x,xs]; + pslg.y=[pslg.y,ys]; + n1=length(pslg.x); + nedges=[[n0+1:n1-1];[n0+2:n1]]'; + pslg.edges=[pslg.edges;nedges]; + nc=nc+1;pslg.chains(nc).nodes=[n0+1:n1]; + pslg.chains(nc).index=k; + pslg.chains(nc).type='starts outside'; + pslg.chains(nc).BI=1; + + end + else % segment starts inside outer boundary + nseg=length(xia); + xs=[xia(end),x( iis(end)+1:end ),x(1:iis(1) ),xia(1)]; + ys=[yia(end),y( iis(end)+1:end ),y(1:iis(1) ),yia(1)]; + + dx=abs(xs(2:end)-xs(1:end-1) +i*[ys(2:end)-ys(1:end-1)] ); + n0=length(pslg.x); + pslg.x=[pslg.x,xs]; + pslg.y=[pslg.y,ys]; + n1=length(pslg.x); + nedges=[[n0+1:n1-1];[n0+2:n1]]'; + pslg.edges=[pslg.edges;nedges]; + nc=nc+1;pslg.chains(nc).nodes=[n0+1:n1]; + pslg.chains(nc).index=k; + pslg.chains(nc).type='starts inside, first seg'; + pslg.chains(nc).BI=1; + + for j=2:2:nseg-1, + xs=[xia(j),x( iis(j):iis(j+1) ),xia(j+1)]; + ys=[yia(j),y( iis(j):iis(j+1) ),yia(j+1)]; + + dx=abs(xs(2:end)-xs(1:end-1) +i*[ys(2:end)-ys(1:end-1)] ); + n0=length(pslg.x); + pslg.x=[pslg.x,xs]; + pslg.y=[pslg.y,ys]; + n1=length(pslg.x); + nedges=[[n0+1:n1-1];[n0+2:n1]]'; + pslg.edges=[pslg.edges;nedges]; + nc=nc+1;pslg.chains(nc).nodes=[n0+1:n1]; + pslg.chains(nc).type='starts inside'; + pslg.chains(nc).index=k; + pslg.chains(nc).BI=1; + + end + end + + else %isempty(xi)--> island entirely inside bounding box + xs=[x(1:end-1)]; + ys=[y(1:end-1)]; + dx=abs(xs(2:end)-xs(1:end-1) +i*[ys(2:end)-ys(1:end-1)] ); + if length(xs)>2,%no degenerate islands + n0=length(pslg.x); + pslg.x=[pslg.x,xs]; + pslg.y=[pslg.y,ys]; + n1=length(pslg.x); + nedges=[[n0+1:n1-1];[n0+2:n1]]'; + nedges=[nedges;[n1,n0+1]]; + pslg.edges=[pslg.edges;nedges]; + nc=nc+1;pslg.chains(nc).nodes=[n0+1:n1,n0+1]; + pslg.chains(nc).type='interior island'; + pslg.chains(nc).index=k; + pslg.chains(nc).BI=0; + end + end + end + end % if n>3, ar> + if mod(k,1000)==0,disp(['Land segments compleate: ',num2str(k/N)]);,end +end + +%OK - now revisit outer boundary! +eval(['save -v7.3 ',FileOutMatlab]); + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Intermission +% restart script from here if needed +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +eval(['load ',FileOutMatlab]); + +nc=length(pslg.chains); +n=length(pslg.x); + +%add southeast, northwest and northeast box corner nodes +%pslg.x=[pslg.x,CornerX]; +%pslg.y=[pslg.y,CornerY]; + +nc=length(pslg.chains); +cc=0; +for k=1:4 + if IsCornerIn(k), + cc=cc+1; + pslg.x(n+cc)=CornerX(k); + pslg.y(n+cc)=CornerY(k); + pslg.chains(nc+cc).nodes=n+cc;%chain pointing to self + pslg.chains(nc+cc).BI=1;%chain on bndy + end +end +nc=length(pslg.chains); +n=length(pslg.x); + +%find end points of chains on boundary +xbn=[]; +ybn=[]; +chn=[];nb=[]; +figure; +for k=1:nc, + if pslg.chains(k).BI==1, + nb=[nb;pslg.chains(k).nodes(1),pslg.chains(k).nodes(end)]; + %chn=[chn;[k,k]]; + chn=[chn;k]; + xbn=[xbn;[pslg.x( pslg.chains(k).nodes(1)), pslg.x( pslg.chains(k).nodes(end))]]; + ybn=[ybn;[pslg.y( pslg.chains(k).nodes(1)), pslg.y( pslg.chains(k).nodes(end))]]; + plot(pslg.x( pslg.chains(k).nodes), pslg.y( pslg.chains(k).nodes)); + hold on + end +end +plot(xbn,ybn,'ro') + +Deps1=10^-10; %tolerance for finding boundary points +%traverse south edge from west to east +[jS,cS]=find(abs(ybn-min(By)) < Deps1); +xS=[]; +yS=[]; +for k=1:length(jS), + xS=[xS,xbn(jS(k),cS(k))]; + yS=[yS,xbn(jS(k),cS(k))]; + if cS(k)==1, + nS(k)=pslg.chains( chn(jS(k)) ) .nodes(1); + else + nS(k)=pslg.chains( chn(jS(k)) ) .nodes(end); + end +end + +[xx,mm]=mink(xS,2);%Find two western most intersections on South boundary + +j0=nS(mm(1)); +j1=nS(mm(2)); + +nc=length(pslg.chains); +pslg.chains(nc+1).nodes=[j0,j1]; +pslg.chains(nc+1).BI=1; +nc=length(pslg.chains); +for k=1:nc + if mod(k,100)==0,disp(['Labeling chains compleate: ',num2str(k/nc)]);end + spx(k)=pslg.x(pslg.chains(k).nodes(1)); + spy(k)=pslg.y(pslg.chains(k).nodes(1)); + epx(k)=pslg.x(pslg.chains(k).nodes(end)); + epy(k)=pslg.y(pslg.chains(k).nodes(end)); + end + + +%The function c runs from lowest on the South West corner of the mesh, +%increasing moving east along the South bounding line. It then increases +%moving North along the East Boundary, then increases moving West Along the +% North Boundary before increaing moving South along the West Boundary. + +%make boundary order index along boundary for start points of outer boundary segments +SN=10.*[max(abs(pslg.x))+max(abs(pslg.y))];%large number to seperate edges S,E, N, W boundaries +c=0*spy; +j=find(abs(spy-By(1))v); +[mm,m]=min(c(j)); +l=j(m); +epi=[epi,pslg.chains(l).nodes]; +obc=[obc,l]; +n=0; +%while(epi(end)~=epi(1)) +while isempty( find(epi(2:end)==epi(1)) ) + v=d(l); + j=find(c>v); + [mm,m]=min(c(j)); + l=j(m) + epi=[epi,pslg.chains(l).nodes]; + obc=[obc,l]; + if isplot==1, plot(pslg.x(epi),pslg.y(epi),'r.-');pause(.01);end +end + +j=find(epi(2:end)==epi(1)); +epi=epi(1:(j+1)); +%vvvvvvvvvvv Add fixed grid points on open boundary +% to prevent "curved" boundaries due to projection details +xob=pslg.x(epi); +yob=pslg.y(epi); +dob=abs([xob(2:end)-xob(1:end-1)]+i*[yob(2:end)-yob(1:end-1)]); +dob=[dob,abs([xob(end)-xob(1)]+i*[yob(1)-yob(end)])]; +nb=length(xob); +dmin=1;%node spacing around boundary +epit=epi; +for k=1:nb-1 + d=abs([xob(k+1)-xob(k)]+i*[yob(k+1)-yob(k)]); + if d>dmin + npl=round(d/dmin)-1; + dx=xob(k+1)-xob(k); + dy=yob(k+1)-yob(k); + xp=xob(k)+dx*[1:npl-1]/npl; + yp=yob(k)+dy*[1:npl-1]/npl; + np=length(xp); + n=length(pslg.x); + pslg.x=[pslg.x,xp]; + pslg.y=[pslg.y,yp]; + NewEdges=[[epi(k),n+1];[[n+1:n+np-1]',[n+2:n+np]'];[n+np,epi(k+1)]]; + %pslg.edges=[pslg.edges;NewEdges]; + j=find(epi(k)==epit ); + epit=[epit(1:j),n+1:n+np,epit(j+1:end)]; + end +end +epi=epit; +% to prevent "curved" boundaries due to projection details +%^^^^^^^^^^^^ Add fixed grid points on open boundary + + +% add edges in obc +epiv=epi(:); +NOE=length(epiv) +OutterEdges=[epiv(1:NOE-1),epiv(2:NOE)]; +pslg.edges=[pslg.edges;OutterEdges]; + +eval(['save -v7.3 ',FileOutMatlab]); + +nodelist=[]; +chn=[]; +for k=1:nc + if mod(k,100)==0,disp(['Finding interior chains compleate: ',num2str(k/nc)]);,end + if pslg.chains(k).nodes(1)==pslg.chains(k).nodes(end) + n=pslg.chains(k).nodes(1); + % [inpoly,onpoly]=insidepoly( pslg.x(n), pslg.y(n),xob,yob); + % if and(inpoly==1,onpoly==0) + inpoly=insidepoly( pslg.x(n), pslg.y(n),xob,yob); + if inpoly==1, + nodelist=union(nodelist,pslg.chains(k).nodes); + chn=[chn,k]; + end + end +end + +eval(['save -v7.3 ',FileOutMatlab]); + +pslg.chains=pslg.chains(chn); +nc=length(pslg.chains); +pslg.chains(nc+1).nodes=epi; + +nodelistXB=union(nodelist,epi); +pslgb=subpslgFast(pslg,nodelistXB); + +%remove "random" duplicate nodes +%z=pslgb.x+i*pslgb.y; +%[zu,j,k]=unique(z); +%pslgc=subpslgFast(pslgb,j); + +pslgc=pslgb + +%remove duplicate edges +pslgc.edgesS=sort(pslgc.edges')'; +pslgc.edgesSU=unique(pslgc.edgesS,'rows') +pslgc.edges=pslgc.edgesSU; + +pslg=pslgc + +eval(['save -v7.3 ',FileOutMatlab]); + +%save PSLG to jigsaw .msh format +geom=pslg2geom(pslg) +savemsh(FileOutJigsaw,geom) + diff --git a/unst_msh_gen/regional/matlab/CombineMesh.m b/unst_msh_gen/regional/matlab/CombineMesh.m new file mode 100644 index 0000000..0caf722 --- /dev/null +++ b/unst_msh_gen/regional/matlab/CombineMesh.m @@ -0,0 +1,20 @@ +function g=CombineMesh(g0,g1); +% function g=CombineMesh(g0,g1); +% Combine 2 non intersecting mesh structures g0 and g1 to get their union g. + +nn=length(g0.x); +g.x=[g0.x(:);g1.x(:)]; +g.y=[g0.y(:);g1.y(:)]; +g.z=[g0.z(:);g1.z(:)]; +g.e=[g0.e;nn+g1.e]; + +if isfield(g0,'bnd') + if isfield(g1,'bnd') + g.bnd=[g0.bnd;g1.bnd+nn]; + else + g.bnd=g0.bnd + end +else + g.bnd=[]; +end + diff --git a/unst_msh_gen/regional/matlab/ComputeAdjacency.m b/unst_msh_gen/regional/matlab/ComputeAdjacency.m new file mode 100644 index 0000000..96536cc --- /dev/null +++ b/unst_msh_gen/regional/matlab/ComputeAdjacency.m @@ -0,0 +1,8 @@ +function Ac=ComputeAdjacency(e) +% function Ac=ComputeAdjacency(e) +% Compute number of adjacencent elements for each node in mesh with element list e +% +% input: e (ne x 3) element list (ne : number of elements in mesh) +% outout: Ac (nn x 1) number of elements adjacent to each node (nn : number of nodes in mesh) + +[Ac,GR] = groupcounts(e(:)); diff --git a/unst_msh_gen/regional/matlab/ComputeLengthScale_wgs84.m b/unst_msh_gen/regional/matlab/ComputeLengthScale_wgs84.m new file mode 100644 index 0000000..a0856a4 --- /dev/null +++ b/unst_msh_gen/regional/matlab/ComputeLengthScale_wgs84.m @@ -0,0 +1,32 @@ +function lengthscale_wgs84=ComputeLengthScale_wgs84_area(x,y,e) +%function ComputeLengthScale(x,y,e) +% Input: +% x -lon [nn x 1] +% y -lat [nn x 1] +% e -element list[ ne x 3] +% +% Output: +% lengthscale [ne x 1] lengthscale of element in m based on +% element area assuming equilateral + +EqTrC=4/sqrt(3); +[ne,three]=size(e) +lengthscale=zeros(1,ne); +wgs84 = wgs84Ellipsoid("km"); +n=length(x); +xp=x; +yp=y; +xp(n+1)=NaN; +yp(n+1)=NaN; +[ne,three]=size(e) +eP=[e(:,1:3),n+1+zeros(ne,1)]; +Xp=xp(eP)'; +Yp=yp(eP)'; +t0=now +clear a +a = areaint(Yp(:),Xp(:),wgs84); +t1=now; +60*24*(t1-t0) +et=60*24*(t1-t0); +disp([' total time : ',num2str(et),' min']); +lengthscale_wgs84=sqrt(a*EqTrC); diff --git a/unst_msh_gen/regional/matlab/ComputeLengthScale_wgs84_MEL.m b/unst_msh_gen/regional/matlab/ComputeLengthScale_wgs84_MEL.m new file mode 100644 index 0000000..9643cf9 --- /dev/null +++ b/unst_msh_gen/regional/matlab/ComputeLengthScale_wgs84_MEL.m @@ -0,0 +1,31 @@ +function lengthscale_wgs84_MEL=ComputeLengthScale_wgs84_MEL(x,y,e) +%function ComputeLengthScale(x,y,e) +% Input: +% x -lon [nn x 1] +% y -lat [nn x 1] +% e -element list[ ne x 3] +% +% Output: +% lengthscale [ne x 1] lengthscale of element in km, +% defined by mean side length +mthd=1 + +x1=x(e(:,1));y1=y(e(:,1)); +x2=x(e(:,2));y2=y(e(:,2)); +x3=x(e(:,3));y3=y(e(:,3)); + +if mthd==1 + wgs84 = wgs84Ellipsoid("km"); + D3 = distance([y1,x1],[y2,x2],wgs84); + D1 = distance([y2,x2],[y3,x3],wgs84); + D2 = distance([y3,x3],[y1,x1],wgs84); + lengthscale_wgs84_MEL=[D1+D2+D3]/3;% mean edge length + % lengthscale_wgs84_MEL=min(min(D1,D2),D3);% shortest edge +else + R=6378.100 %radius of earth in km + D03 = distance([y1,x1],[y2,x2],'degrees'); + D01 = distance([y2,x2],[y3,x3],'degrees'); + D02 = distance([y3,x3],[y1,x1],'degrees'); + D0=[D01+D02+D03]/3; + lengthscale_wgs84_MEL=R*sin(D0*pi/180); +end \ No newline at end of file diff --git a/unst_msh_gen/regional/matlab/DistanceToCoast.m b/unst_msh_gen/regional/matlab/DistanceToCoast.m new file mode 100644 index 0000000..55b7b7f --- /dev/null +++ b/unst_msh_gen/regional/matlab/DistanceToCoast.m @@ -0,0 +1,58 @@ +function D=DistanceToCoast(lon,lat,lonP,latP) +%function D=DistanceToCoast(lon,lat,lonP,latP) +% Computes grided distance to pointset on Globe +% +% Computes shortest distance to pointset [lonP,latP] from coordinates of +% lat by lon grid in m. +% +% inputs: +% lon - [nx,1] lon coordinates for output matrix +% lat - [ny,1] lon coordinates for output matrix +% lonP - [nn,1] longitude coordiantes of point set +% latP - [nn,1] latitude coordiantes of point set +% +% outputs: +% D - [ny, nx] units meters +% D(j,k)= minimumt Distance from point lat(j), lon(k) to pointset (lonP,latP) +% +clear d0 +lat2m=single(110574.) +lon=single(lon); +lat=single(lat); +nx=length(lon) +ny=length(lat) +clear ld1 ld2 D; +t0=now; +LambdaBox=10;%only use points within 5 degrees lat for 10x+ speed up +latP=latP(:); +lonP=lonP(:); + +lonP=LonCon(lonP); +lon=LonCon(lon); + +for j=1:ny % this loop takes ~16 hours + t00=now; + j/ny + lon2m=single(111320.*cos(lat(j)*pi/180)); + jbox=find(abs(lat(j)-latP)1 + if(ComputeBndy) + bnd=detbndy(e);%find boundary elements including islands + jb=unique(bnd(:)) + OpenBndNodes=intersect(jb,jc); + end +end diff --git a/unst_msh_gen/regional/matlab/FindOuterBndWW3.m b/unst_msh_gen/regional/matlab/FindOuterBndWW3.m new file mode 100644 index 0000000..1627c19 --- /dev/null +++ b/unst_msh_gen/regional/matlab/FindOuterBndWW3.m @@ -0,0 +1,47 @@ +function OpenBndNodes=FindOuterBndWW3(g,ComputeBndy) +% function OpenBndNodes=FindOuterBndWW3(meshflin,ComputeBndy) +% find the open boundary nodes from jigsaw format .msh file +% representing an unstructured mesh, and find the estimated +% open ocean boundary nodes assuming the mesh is on an oriented +% rectangle. +% +% inputs: +% meshflin : jigsaw format .msh file representing an unstructured mesh +% ComputeBndy : ComputeBndy =1 in all cases +% output: +% OpenBndNodes : list of open ocean boundary nodes numbers +% +Dmin=4000;%critical distance in meters to find boundary nodes + +%g=loadmsh(meshflin) +if isfield(g,'x') + x=g.x;y=g.y;z=g.z;e=g.e; +else + e=g.tria3.index(:,1:3); + x=g.point.coord(:,1);y=g.point.coord(:,2);f=g.point.coord(:,3); +end + +%assume rectangular mesh +x0=min(x);x1=max(x); +y0=min(y);y1=max(y); + +lat2m=110574. +lon2m=111320. + +DIE=[lon2m*(x-x0).*cos(pi*y/180),... + lon2m*(x1-x).*cos(pi*y/180),... + lat2m*(y-y0),lat2m*(y1-y)]; +DIE=abs(DIE); +D=min(DIE'); + +jc=find(D1 + if(ComputeBndy) + bnd=detbndy(e);%find boundary elements including islands + jb=unique(bnd(:)) + OpenBndNodes=intersect(jb,jc); + end +end diff --git a/unst_msh_gen/regional/matlab/FindPointsAx.m b/unst_msh_gen/regional/matlab/FindPointsAx.m new file mode 100644 index 0000000..173f801 --- /dev/null +++ b/unst_msh_gen/regional/matlab/FindPointsAx.m @@ -0,0 +1,14 @@ +function j=FindPointsAx(ax,x,y); +%function j=FindPointsAx(ax,x,y); +% returns indexs j for points (x,y) inside matlab axis object ax +% useage example: +% +% >>ax=axis % get current figure axis +% >>j=FindPointsAx(ax,g.x,g.y); +% >>g_local=submeshFast(g,j) +% +% to return mesh structure contained within current figure axis + +jx=find(and(x>ax(1),xax(3),yax(1),g.xax(3),g.yax(1),p.xax(3),p.y3 + [xs,ys]=SmoothSubSampleCoastlineFast(x,y,interpDist,SmoothN); + xus=[xus,xs]; + yus=[yus,ys]; + end +end + +whos x0 xus + +xus=[xus(:)',xtrgt(:)']; +yus=[yus(:)',ytrgt(:)']; + +n0=length(xus); + +p=loadmsh(PSLGfile); +p.x=p.point.coord(:,1); +p.x=LonCon(p.x); + +p.y=p.point.coord(:,2); +p.edges=p.edge2.index(:,1:2); + +xg=min(p.x-DX):DX:max(p.x+DX); +yg=min(p.y-DX):DX:max(p.y+DX); +nx=length(xg); +ny=length(yg); +XG=ones(ny,1)*[xg(:)']; +YG=yg(:)*ones(1,nx); + +topo=BoxSmoothTopo(GlobalTopoFile,0); +lon = topo.point.coord{:,1}; +lat = topo.point.coord{:,2}; + +localtopo=topo; +localtopo.point.coord{:,1}=xg(:); +localtopo.point.coord{:,2}=yg(:); +localtopo.value=interp2(topo.point.coord{:,1},topo.point.coord{:,2},topo.value,XG,YG); + +%This loop Takes a few min:Find PSLG points near us coastline +deg2km=111.132954 +np=length(p.x); +NP=10000; + +clear d; +nus=length(xus); +for k=1:NP:np + j=k:min(np,k+NP); + x=p.x(j); + y=p.y(j); + d(j)=min(deg2km*abs([x(:)-xus(:)'].*[cos(pi*y(:)/180)*ones(1,nus)]+i*[y(:)-yus(:)'])'); + %if mod(k,1000)==0,k/np,end + k/np +end + +%dmin=10; +dmin=50; +j=find(dax(1),g.xax(3),g.yx0;g0.x(:)'y0;g0.y(:)'0, + hold on;plot(x(deadnodes),y(deadnodes),'ko',x(deadnodes),y(deadnodes),'kx'); + title('Yo! nodes now exist with no elements! Fix with remove_dead_nodes'); +end + +set(ph,'EdgeColor','w');set(ph,'EdgeAlpha',.2); +axis(ax) +whos jg rmi +max(rmi) +jb=jg(rmi); +nn=length(g.x); +gnew=submeshFast(g,setdiff(1:nn,jb)); + + diff --git a/unst_msh_gen/regional/matlab/RemoveMissingIslands.m b/unst_msh_gen/regional/matlab/RemoveMissingIslands.m new file mode 100644 index 0000000..3e4b805 --- /dev/null +++ b/unst_msh_gen/regional/matlab/RemoveMissingIslands.m @@ -0,0 +1,94 @@ +function gnew=RemoveMissingIslands(g,ax,p,MinBndDist,isplot); +%function gnew=RemoveMissingIslands(g,ax,p,MinBndDist,isplot); +% Remove nodes of mesh, g inside closed curves defined in pslg +% p. The action is preformed only within the axis ax. +% This wont touch nodes within MinBndDist (m) of existing boundary +% input: +% g : FE mesh structure with fields +% g.x : longitute +% g.y : latitude +% g.z : bathymetric depth +% g.e : (ne x 3) element list +% ax : output of axis for figure, i.e. ax=axis or prespecified as ax=[xmin, xmax,ymin, ymax] +% p: a Planar Straight Line Graph (pslg) structure with fields +% p.x : (nn x 1) x coordinates of nodes +% p.y : (nn x 1 )y coordinates of nodes +% p.edges : (nedges x 2) list of edges between nodes +% isplot=0 for no ploting and isplot=1 to plot mesh editing +% + + +if nargin<4 + MinBndDist=1000; +end +if nargin <5, + isplot=0; +end +jx=find(and(g.x>ax(1),g.xax(3),g.yax(1),p.xax(3),p.y2, + if n(1)==n(end), + jins=find(inside(x,y,p0.x(n),p0.y(n))); + jb0=[jb0;jins(:)]; + end + end +end + +lat2m=110574.; +lon2m=111320.; + +jb0X=[]; +for k=1:length(jb0); + n=jb0(k); + + d=min(abs( lon2m*( x(n)-xb )*cos(pi*y(n)/180) + ... + i*lat2m*( y(n)-yb ) )); + if d>MinBndDist,jb0X=[jb0X,n];end +end +jb0=jb0X; + +if isplot, + hold on + plot(x(jb0),y(jb0),'ro'); +end + nn=length(x); +jg0=setdiff(1:nn,jb0); +g1=submeshFast(g0,jg0); + +if isplot, + figure; + x=g1.x;y=g1.y;z=g1.z;e=g1.e; + clf;ph=patch(x(e'),y(e'),z(e'));cm=colormap('jet');shading interp;colorbar;axis equal; + hold on;plot(p0.x(p0.edges'),p0.y(p0.edges'),'k.-'); + set(ph,'EdgeColor','w');set(ph,'EdgeAlpha',.2); +end +jb=jg(jb0); +nn=length(g.x); +gnew=submeshFast(g,setdiff(1:nn,jb)); + + diff --git a/unst_msh_gen/regional/matlab/RemoveMissingIslandsEle.m b/unst_msh_gen/regional/matlab/RemoveMissingIslandsEle.m new file mode 100644 index 0000000..82dd7e2 --- /dev/null +++ b/unst_msh_gen/regional/matlab/RemoveMissingIslandsEle.m @@ -0,0 +1,120 @@ +function gnew=RemoveMissingIslandsEle(g,ax,p,MinBndDist,isplot); +%function gnew=RemoveMissingIslandsEle(g,ax,p,MinBndDist,isplot); +% Remove nodes of mesh, g inside closed curves defined in pslg +% p. Only nodes in which all adjacent elements are with a closed +% curve are removed. The action is preformed only within the axis ax. +% This wont touch nodes within MinBndDist (m) of existing boundary +% input: +% g : FE mesh structure with fields +% g.x : longitute +% g.y : latitude +% g.z : bathymetric depth +% g.e : (ne x 3) element list +% ax : output of axis for figure, i.e. ax=axis or prespecified as ax=[xmin, xmax,ymin, ymax] +% p: a Planar Straight Line Graph (pslg) structure with fields +% p.x : (nn x 1) x coordinates of nodes +% p.y : (nn x 1 )y coordinates of nodes +% p.edges : (nedges x 2) list of edges between nodes +% MinBndDist : distance in meters from p in which mesh nodes in g will not be removed +% isplot=0 for no ploting and isplot=1 to plot mesh editing +% output: +% gnew : FE mesh structure with nodes inside curves defined by p removed +% + +if nargin<4 + MinBndDist=1000; +end +if nargin <5, + isplot=0; +end +jx=find(and(g.x>ax(1),g.xax(3),g.yax(1),p.xax(3),p.y2, + if n(1)==n(end), + %jins=find(inside(x,y,p0.x(n),p0.y(n))); + %jins=find(inside(x,y,p0.x(n),p0.y(n))==1); + [inP,onP]=insidepoly(x,y,p0.x(n),p0.y(n)); + jins=find(or(inP,onP)); + + jb0=[jb0;jins(:)]; + IsClosed(k)=1; + hold on;plot(p0.x(n),p0.y(n),'gx-') + end + end + end + + lat2m=110574.; + lon2m=111320.; + + jb0X=[]; + for k=1:length(jb0); + n=jb0(k); + + d=min(abs( lon2m*( x(n)-xb )*cos(pi*y(n)/180) + ... + i*lat2m*( y(n)-yb ) )); + if d>MinBndDist,jb0X=[jb0X,n];end + end + jb0=jb0X; + + if isplot, + hold on + plot(x(jb0),y(jb0),'ro'); + end + + [g1,kr0]=submeshFastEle(g0,jb0); + + if isplot, + figure(2); + x=g1.x;y=g1.y;z=g1.z;e=g1.e; + clf;ph=patch(x(e'),y(e'),z(e'));cm=colormap('jet');shading interp;colorbar;axis equal; + hold on;plot(p0.x(p0.edges'),p0.y(p0.edges'),'k.-'); + set(ph,'EdgeColor','w');set(ph,'EdgeAlpha',.2); + end + %jb=jg(jb0); + kr1=intersect(kr0,jb0);% kr0 can have boundary nodes not intended for removal!! + if isplot, + figure(2); + XE=g0.x(kr1); + YE=g0.y(kr1); + plot(XE,YE,'md') + figure(1);hold on;plot(XE,YE,'md') + end + jb=jg(kr1); + nn=length(g.x); + gnew=submeshFast(g,setdiff(1:nn,jb)); %only removes nodes + else + gnew=g; + end +else + gnew=g; +end + diff --git a/unst_msh_gen/regional/matlab/RemoveMissingIslandsPoint.m b/unst_msh_gen/regional/matlab/RemoveMissingIslandsPoint.m new file mode 100644 index 0000000..e94e68e --- /dev/null +++ b/unst_msh_gen/regional/matlab/RemoveMissingIslandsPoint.m @@ -0,0 +1,91 @@ +function gnew=RemoveMissingIslandsPoint(g,ax,p,MinBndDist); +% function gnew=RemoveMissingIslandsPoint(g,ax,p,MinBndDist); +% Not in current use. This will be removed upon further review + +if nargin<4 + MinBndDist=1000; +end + +jx=find(and(g.x>ax(1),g.xax(3),g.yax(1),p.xax(3),p.y2, + dx=p.x(n(1:end-1))-p.x(n(2:end)); + dy=p.y(n(1:end-1))-p.y(n(2:end)); + d=abs( lon2m*dx*cos(pi*mean(p.y(n))/180)+i*lat2m*(dy) ); + P=sum(d)/1000;%perimeter length of feature + mx=mean(p0.x(n)); + my=mean(p0.y(n)); + [m,jc]=min(abs(g0.x-mx+i*[g0.y-my] )); + P + LSn(jc) + + if LSn(jc)*6 < P, %if "new island" smaller than feature- cut it + hold on;plot(mx,my,'ro',g0.x(jc),g0.y(jc),'cx'); + jb0=[jb0;jc]; + else + hold on;plot(mx,my,'r.',g0.x(jc),g0.y(jc),'rx'); + end + end +end +jc +jb0 +lat2m=110574. +lon2m=111320. + +jb0X=[]; +for k=1:length(jb0); + n=jb0(k); + + d=min(abs( lon2m*( x(n)-xb )*cos(pi*y(n)/180) + ... + i*lat2m*( y(n)-yb ) )); + if d>=MinBndDist,jb0X=[jb0X,n];end +end +jb0=jb0X + +hold on +plot(x(jb0),y(jb0),'ro'); +xjb0=x(jb0); +yjb0=y(jb0); +nn=length(x); +jg0=setdiff(1:nn,jb0); +g1=submeshFast(g0,jg0); + +figure; +x=g1.x;y=g1.y;z=g1.z;e=g1.e; +clf;ph=patch(x(e'),y(e'),z(e'));cm=colormap('jet');shading interp;colorbar;axis equal; +hold on;plot(p0.x(p0.edges'),p0.y(p0.edges'),'k.-') +set(ph,'EdgeColor','w');set(ph,'EdgeAlpha',.2); +plot(xjb0,yjb0,'r.'); + +jb=jg(jb0); +nn=length(g.x); +gnew=submeshFast(g,setdiff(1:nn,jb)); + + diff --git a/unst_msh_gen/regional/matlab/RemoveSandPoints.m b/unst_msh_gen/regional/matlab/RemoveSandPoints.m new file mode 100644 index 0000000..adb75ce --- /dev/null +++ b/unst_msh_gen/regional/matlab/RemoveSandPoints.m @@ -0,0 +1,81 @@ +function RemoveSandPoints(FileInJigsawMesh,FileInJigsawPSLG,FileOutJigsawMesh,FileOutWW3) +%function RemoveSandPoints(FileInJigsawMesh,FileInJigsawPSLG,FileOutJigsawMesh,FileOutWW3) +% Find and remove sandpoints from fininte element mesh structure g +% Because removal of sand points can create new sand points, the process is run iteratively +% within this routine. Usually, 1-3 iterations are required to clear all sandpoints in large +% ~ 5 million node meshes generated with jigsaw. + +% A sandpoint is a boundary node with more than two adjacent boundary nodes. Sandpoints are +% flagged in WW3 and WW3 will not run with a mesh containing sandpoints(error in grid.out). +% +% input: +% FileInJigsawMesh : jigsaw format .msh file describing a finite element mesh(jigsaw output) +% FileInJigsawPSLG : jigsaw format .msh file describing the geometry of the PSLG used +% to create the mesh defined in FileInJigsawMesh +% FileOutJigsawMesh : jisaw format .msh file with sandpoints removed +% FileOutWW3 : WW3 .msh file with sandpoints removed +% + +%g=loadmsh('output/RWPS.F.LLH.msh'); +%g=loadmsh('../RWPSMeshTest/output.1km.10km/RWPS.F.LLH.msh'); +g=loadmsh(FileInJigsawMesh) + +close all; +count=0; +IsValidBoundary=0 +while ~IsValidBoundary, + e=g.tria3.index(:,1:3); + x=g.point.coord(:,1);y=g.point.coord(:,2);f=g.point.coord(:,3); + bnd=detbndy(e); + bndn=unique(bnd(:)); + clear n1 n2 + for k=1:length(bndn); + j1=find(bndn(k)==bnd(:,1)); + j2=find(bndn(k)==bnd(:,2)); + n1(k)=length(j1); + n2(k)=length(j2); + end + + nb=n1+n2; + unique(nb)% 2 or 4 + + j4=find(nb==4) + if isempty(j4) + IsValidBoundary=1 + end + + figure; + clf;patch(x(e'),y(e'),f(e'));shading interp;colormap('jet');colorbar; + hold on + phsp=plot(x(bndn(j4)),y(bndn(j4)),'ro'); + title(['iteration : ',int2str(count)]) + if ~IsValidBoundary, + count=count+1; + bb=bndn(j4) + bbu=unique(bb) + display(['number of bad boundary points= ',int2str(length(bbu))]) + display(['iteration number : ',int2str(count)]) + nn=length(x); + %jgn=setdiff([1:nn],bbu(:)'); + + jgnBnd=setdiff([1:nn],bbu(:)'); + g.x=x(:);g.y=y(:);g.z=f(:);g.e=e; + hBnd=submeshFast(g,jgnBnd);%can orphan nodes + jgnInt=unique(hBnd.e(:));%nodes still in an element + h=submeshFast(hBnd,jgnInt); + + [ne,three]=size(h.e) + nn=length(h.x) + g0=g;%put back into jigsaw format + g0=rmfield(g0,"tria3") + g0=rmfield(g0,"point") + ep=[h.e,zeros(ne,1)]; + g0.tria3.index=ep; + g0.point.coord=[h.x(:),h.y(:),h.z(:),zeros(nn,1)]; + g0.value=-h.z; + g=g0; + end +end + +savemsh(FileOutJigsawMesh,g); +WriteWW3Mesh(FileOutJigsawMesh,FileInJigsawPSLG,FileOutWW3); diff --git a/unst_msh_gen/regional/matlab/RemoveSandPointsWW3.m b/unst_msh_gen/regional/matlab/RemoveSandPointsWW3.m new file mode 100644 index 0000000..0cd1090 --- /dev/null +++ b/unst_msh_gen/regional/matlab/RemoveSandPointsWW3.m @@ -0,0 +1,73 @@ +function g=RemoveSandPointsWW3(g,FileOutWW3) +%function g=RemoveSandPointsWW3(g,FileOutWW3) +% Find and remove sandpoints from fininte element mesh structure g +% Because removal of sand points can create new sand points, the process is run iteratively +% within this routine. Usually, 1-3 iterations are required to clear all sandpoints in large +% ~ 5 million node meshes generated with jigsaw. + +% A sandpoint is a boundary node with more than two adjacent boundary nodes. Sandpoints are +% flagged in WW3 and WW3 will not run with a mesh containing sandpoints(error in grid.out). +% +% input: +% g : FE mesh structure with fields +% g.x : longitute +% g.y : latitude +% g.z : bathymetric depth +% g.e : (ne x 3) element list +% FileOutWW : file name to write WW3 msh file for g +% +% outputs: +% g : input mesh with all sandpoints removed by deletion +% + +close all; +count=0; +IsValidBoundary=0 +while ~IsValidBoundary, + x=g.x;y=g.y;f=g.z;e=g.e; + bnd=detbndy(e); + bndn=unique(bnd(:)); + clear n1 n2 + for k=1:length(bndn); + j1=find(bndn(k)==bnd(:,1)); + j2=find(bndn(k)==bnd(:,2)); + n1(k)=length(j1); + n2(k)=length(j2); + end + + nb=n1+n2; + unique(nb)% 2 or 4 + + j4=find(nb==4) + if isempty(j4) + IsValidBoundary=1 + end + + figure; + clf;patch(x(e'),y(e'),f(e'));shading interp;colormap('jet');colorbar; + hold on + plot(x(bndn(j4)),y(bndn(j4)),'ro'); + title(['iteration : ',int2str(count)]) + if ~IsValidBoundary, + count=count+1; + bb=bndn(j4) + bbu=unique(bb) + display(['number of bad boundary points= ',int2str(length(bbu))]) + display(['iteration number : ',int2str(count)]) + nn=length(x); + %jgn=setdiff([1:nn],bbu(:)'); + + jgnBnd=setdiff([1:nn],bbu(:)'); + g.x=x(:);g.y=y(:);g.z=f(:);g.e=e; + hBnd=submeshFast(g,jgnBnd);%can orphan nodes + jgnInt=unique(hBnd.e(:));%nodes still in an element + h=submeshFast(hBnd,jgnInt); + [ne,three]=size(h.e) + nn=length(h.x) + g=h; + end +end + +if nargin >1, + WriteWW3MeshX(g,FileOutWW3); +end diff --git a/unst_msh_gen/regional/matlab/SmoothSubSampleCoastlineFast.m b/unst_msh_gen/regional/matlab/SmoothSubSampleCoastlineFast.m new file mode 100644 index 0000000..ea92c4c --- /dev/null +++ b/unst_msh_gen/regional/matlab/SmoothSubSampleCoastlineFast.m @@ -0,0 +1,28 @@ +function [xss,yss]=SmoothSubSampleCoastlineFast(x,y,DI,lambda); +% function [xss,yss]=SmoothSubSampleCoastline(x,y,dsmooth,lambda); +% Smooth and subsample coastline at dsmooth (m) distance. The coastline is first interpolated +% to uniform spacing and then a boxcar smoother of width DI*(2*lambda+1)is applied. +% Inputs: +% x,y : longitude, latitude points along a coastline. +% If the coastline defines an island +% then x(1)==x(end) and y(1)==y(end), +% DI: distance in meters to interpolate the coastline to +% lambda : integer coastline is smoothed to DI*(2*lambda+1) and resampled lambda*DI spacing +% outputs : +% xss,yss : longitude, latitude points the smoothed and subsampled coastline +% +itz=0; +[xi,yi]=InterpCoastline(x,y,DI,itz); +gamma=2*lambda+1; +ic=lambda+1; +W=ones(1,gamma)/gamma; +xs=conv(xi,W,'same'); +ys=conv(yi,W,'same'); +xss=xs(ic:lambda:end-ic); +yss=ys(ic:lambda:end-ic); +if and(x(1)==x(end),y(1)==y(end)) + if length(xss)>0, + xss=[xss,xss(1)]; + yss=[yss,yss(1)]; + end +end diff --git a/unst_msh_gen/regional/matlab/WriteWW3Mesh.m b/unst_msh_gen/regional/matlab/WriteWW3Mesh.m new file mode 100644 index 0000000..1e3a6a1 --- /dev/null +++ b/unst_msh_gen/regional/matlab/WriteWW3Mesh.m @@ -0,0 +1,81 @@ +function WriteWW3Mesh(mshFileInJigsawFormat,pslgFilelInJigsawFormat,WW3FileOut) +% function WriteWW3Mesh(mshFileInJigsawFormat,pslgFilelInJigsawFormat,WW3FileOut) +% or +% function WriteWW3Mesh(mshFileInJigsawFormat,[],WW3FileOut) +%Convert unstructured triangular mesh in jigsaw format to WW3 .msh format +% +% inputs: +% mshFileInJigsawFormat (input file name) +% pslgFilelInJigsawFormat (not used can be empty) +% WW3FileOut (output file name) +% + + +OpenBndNodes=FindOuterBndNoPSLG(mshFileInJigsawFormat); + +g=loadmsh(mshFileInJigsawFormat) + +x=g.point.coord(:,1);y=g.point.coord(:,2);z=g.point.coord(:,3); +e=g.tria3.index(:,1:3); + +nn=length(x) +[ne,three]=size(e) +nb=length(OpenBndNodes); + +fp=fopen(WW3FileOut,'w'); +fprintf(fp,'$MeshFormat\n') +fprintf(fp,'2 0 8\n') +fprintf(fp,'$EndMeshFormat\n') +fprintf(fp,'$Nodes\n') +fprintf(fp,'%i\n',nn) + +A=[[1:nn]' x(:),y(:),z(:)]; + +fprintf(fp,'%i %f %f %f\n',A') + +fprintf(fp,'$EndNodes\n') +fprintf(fp,'$Elements\n') +fprintf(fp,'%i\n',ne+nb) + +B=[[1:nb]', 15*ones(nb,1) ,2*ones(nb,1),zeros(nb,1),zeros(nb,1),OpenBndNodes(:)]; + +fprintf(fp,'%i %i %i %i %i %i\n',B') + +C=[ nb+[1:ne]',2*ones(ne,1) ,3*ones(ne,1),zeros(ne,1),[1:ne]',zeros(ne,1),e]; +fprintf(fp,'%i %i %i %i %i %i %i %i %i\n',C') +fprintf(fp,'$EndElements\n') +fclose(fp); + + + +%76 2 3 0 1 0 77 76 1 +%$MeshFormat +%2 0 8 +%$EndMeshFormat +%$Nodes +%3070 +%1 -72.0576782709 40.9902316949 4.2878041267 +%2 -72.0521937363 40.9713426805 13.8250312805 +%3 -72.0469687227 40.9523807323 20.9117679596 + +%3068 -72.585996 40.813074 1.747097373 +%3069 -72.586352 40.818835 1.5 +%3070 -72.589697 40.813418 1.5 +%$EndNodes +%$Elements +%5855 +%1 15 2 0 0 75 +%2 15 2 0 0 74 +%3 15 2 0 0 73 + +%73 15 2 0 0 3 +%74 15 2 0 0 2 +%75 15 2 0 0 1 +%76 2 3 0 1 0 77 76 1 +%77 2 3 0 2 0 76 2 1 +%78 2 3 0 3 0 78 2 76 + +%5854 2 3 0 5779 0 3065 3069 3067 +%5855 2 3 0 5780 0 3068 3067 3070 +%$EndElements + diff --git a/unst_msh_gen/regional/matlab/WriteWW3MeshX.m b/unst_msh_gen/regional/matlab/WriteWW3MeshX.m new file mode 100644 index 0000000..6c14a2f --- /dev/null +++ b/unst_msh_gen/regional/matlab/WriteWW3MeshX.m @@ -0,0 +1,77 @@ +function WriteWW3MeshX(g,WW3FileOut) +% function WriteWW3Mesh(g,WW3FileOut) +% Output unstructured mesh to WW3 .msh format +% +% inputs: +% g - finite element mesh structure with fields: +% g.x (nn x 1) longitude coordinates of nodes +% g.y (nn x 1) latitude coordinates of nodes +% g.e (ne x 3) element matrix +% WW3FileOut (output file name) +% + +OpenBndNodes=FindOuterBndWW3(g,1) + +x=g.x;y=g.y;z=g.z;e=g.e; + +nn=length(x) +[ne,three]=size(e) +nb=length(OpenBndNodes); + +fp=fopen(WW3FileOut,'w'); +fprintf(fp,'$MeshFormat\n') +fprintf(fp,'2 0 8\n') +fprintf(fp,'$EndMeshFormat\n') +fprintf(fp,'$Nodes\n') +fprintf(fp,'%i\n',nn) + +A=[[1:nn]' x(:),y(:),z(:)]; + +fprintf(fp,'%i %f %f %f\n',A') + +fprintf(fp,'$EndNodes\n') +fprintf(fp,'$Elements\n') +fprintf(fp,'%i\n',ne+nb) + +B=[[1:nb]', 15*ones(nb,1) ,2*ones(nb,1),zeros(nb,1),zeros(nb,1),OpenBndNodes(:)]; + +fprintf(fp,'%i %i %i %i %i %i\n',B') + +C=[ nb+[1:ne]',2*ones(ne,1) ,3*ones(ne,1),zeros(ne,1),[1:ne]',zeros(ne,1),e]; +fprintf(fp,'%i %i %i %i %i %i %i %i %i\n',C') +fprintf(fp,'$EndElements\n') +fclose(fp); + + + +%76 2 3 0 1 0 77 76 1 +%$MeshFormat +%2 0 8 +%$EndMeshFormat +%$Nodes +%3070 +%1 -72.0576782709 40.9902316949 4.2878041267 +%2 -72.0521937363 40.9713426805 13.8250312805 +%3 -72.0469687227 40.9523807323 20.9117679596 + +%3068 -72.585996 40.813074 1.747097373 +%3069 -72.586352 40.818835 1.5 +%3070 -72.589697 40.813418 1.5 +%$EndNodes +%$Elements +%5855 +%1 15 2 0 0 75 +%2 15 2 0 0 74 +%3 15 2 0 0 73 + +%73 15 2 0 0 3 +%74 15 2 0 0 2 +%75 15 2 0 0 1 +%76 2 3 0 1 0 77 76 1 +%77 2 3 0 2 0 76 2 1 +%78 2 3 0 3 0 78 2 76 + +%5854 2 3 0 5779 0 3065 3069 3067 +%5855 2 3 0 5780 0 3068 3067 3070 +%$EndElements + diff --git a/unst_msh_gen/regional/matlab/detbndy.m b/unst_msh_gen/regional/matlab/detbndy.m new file mode 100644 index 0000000..55c3f10 --- /dev/null +++ b/unst_msh_gen/regional/matlab/detbndy.m @@ -0,0 +1,99 @@ +% DETBNDY compute a boundary segment list for a FEM domain +% +% DETBNDY bnd=detbndy(e); +% This function computes a boundary for the FEM domain +% described a file containing element connectivity list (e). +% It uses sparse matrix techniques to determine the element +% edges on the boundary of the FEM domain. +% +% Input: ele - element list; 3 (.tri) or 4 (.ele) columns wide +% Output: bnd - a 2-column list of boundary-node numbers, returned +% to the local workspace +% +% The output boundary list are pairs of node numbers, not +% coordinates, describing the edges of elements on the +% exterior of the domain, including islands. The segments +% are not connected. +% +% Call as: bnd=detbndy(e); +% +% Written by : Brian O. Blanton at The University of North Carolina +% at Chapel Hill, Mar 1995. +% +function bnd=detbndy(in) + +% DEFINE ERROR STRINGS +err1=['Only one input argument to DETBNDY. Type "help detbndy"']; +err2=['Element list passed to DETBNDY does not have 3 or 4 columns']; +err3=str2mat(['??? Error using ==> detbndy'],... + ['DETBNDY must have one output argument.'],... + ['Call as: >> bnd=detbndy(in);'],... + [' ']); +% check argument list +if nargin~=1 + error(err1); +end + +if nargout~=1 + disp(err3); + return +end + + +% Check size of element list +[nelems,ncol]=size(in); +if ncol < 3 | ncol > 4 + error(err2); + return +elseif ncol==4 + in=in(:,2:4); +end + +% Form (i,j) connection list from .ele element list +% +i=[in(:,1);in(:,2);in(:,3)]; +j=[in(:,2);in(:,3);in(:,1)]; + +% Form the sparse adjacency matrix and add transpose. +% +n = max(max(i),max(j)); +A = sparse(i,j,-1,n,n); +A = A + A'; + +% Consider only the upper part of A, since A is symmetric +% +A=A.*triu(A); + +% The boundary segments are A's with value == 1 +% +B=A==1; + +% Extract the row,col from B for the boundary list. +% +[ib,jb,s]=find(B); +bnd=[ib(:),jb(:)]; + +% Output .bnd list +% +%res=input('Output .bnd boundary list (y/n) ? [n] ','s'); +%if strcmp(res,'y') +% fname=input('Enter .bnd filename [bnd.dat] ','s'); +% if isempty(fname),fname='bnd.dat';,end +% fp=fopen(fname,'w'); +% for i=1:length(bnd) +% fprintf(fp,'%d %d\n', bnd(i,1),bnd(i,2)); +% end +%end +% +% Brian O. Blanton +% Curriculum in Marine Sciences +% 15-1A Venable Hall +% CB# 3300 +% University of North Carolina +% Chapel Hill, NC +% 27599-3300 +% +% 919-962-4466 +% blanton@marine.unc.edu +% + diff --git a/unst_msh_gen/regional/matlab/edges2chains.m b/unst_msh_gen/regional/matlab/edges2chains.m new file mode 100644 index 0000000..130e187 --- /dev/null +++ b/unst_msh_gen/regional/matlab/edges2chains.m @@ -0,0 +1,87 @@ +function chains=edges2chains(edges); +%edges2chains::decompose list of edges into all maximal chains and cycles(non edge repeating paths) +%%take a Nx2 adjacency list and decompose it into all maximum cylcles and chains with provided no +%vertex has order greater than 2. +%function chains=edges2chains(edges); +% +%Inputs: +% edges:(Nedgesx2) integer adjacency list defining edges in graph. +% +%Outputs: +% chains:list of structures defining maximal paths. +% chains(k).nodes:n_k length integer list of nodes in chain k. +% +%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +%Keston Smith & Ata Bilgili May 2001 +%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + + +if ~isempty(edges) + edges=[min(edges')',max(edges')']; + [i,j]=sort(edges(:,1)); + + edges=[edges(j,1),edges(j,2)]; + + [N,NN]=size(edges); + chains=[]; + n=0; + while ~isempty(edges) + chain=edges(1,1); + j=edges(1,2); + edges=edges(2:end,:); + %while ~isempty(j), + while length(j)==1, + chain=[chain,j(:)']; + %whos edges j + k1=find(edges(:,1)==j); + k2=find(edges(:,2)==j); + if ~isempty(k1), + j=edges(k1,2); + [N,M]=size(edges); + edges=edges(setdiff(1:N,k1),:); + elseif ~isempty(k2), + j=edges(k2,1); + [N,M]=size(edges); + edges=edges(setdiff(1:N,k2),:); + else + j=[]; + end + end + j=chain(1); + k1=find(edges(:,1)==j); + k2=find(edges(:,2)==j); + if ~isempty(k1), + j=edges(k1,2); + [N,M]=size(edges); + edges=edges(setdiff(1:N,k1),:); + elseif ~isempty(k2), + j=edges(k2,1); + [N,M]=size(edges); + edges=edges(setdiff(1:N,k2),:); + else + j=[]; + end +% while ~isempty(j), + while length(j)==1, + + chain=[j,chain]; + k1=find(edges(:,1)==j); + k2=find(edges(:,2)==j); + if ~isempty(k1), + j=edges(k1,2); + [N,M]=size(edges); + edges=edges(setdiff(1:N,k1),:); + elseif ~isempty(k2), + j=edges(k2,1); + [N,M]=size(edges); + edges=edges(setdiff(1:N,k2),:); + else + j=[]; + end + end + n=n+1; + chains(n).nodes=chain; + end +else + chains(1).nodes=[]; +end diff --git a/unst_msh_gen/regional/matlab/edges2chainsG.m b/unst_msh_gen/regional/matlab/edges2chainsG.m new file mode 100644 index 0000000..9d64e0c --- /dev/null +++ b/unst_msh_gen/regional/matlab/edges2chainsG.m @@ -0,0 +1,18 @@ +function chains=edges2chainsG(edges); +%function chains=edges2chainsG(edges); +% This performs similair function to edges2chains, how ever it uses matlabs graph theory library +% and only returns closed cycles rather than cycles and open ended chains. +% Somewhat faster than origonal edges2chains.m (~x 2 speedup) +% + +G=graph(edges(:,1),edges(:,2)); +cycles = allcycles(G); +cc=0; +for k=1:length(cycles), + n=cycles{k}; + if length(n)>2, + n=[n(:);n(1)]; + cc=cc+1; + chains(cc).nodes=n; + end +end diff --git a/unst_msh_gen/regional/matlab/insidepoly.m b/unst_msh_gen/regional/matlab/insidepoly.m new file mode 100644 index 0000000..22e69f7 --- /dev/null +++ b/unst_msh_gen/regional/matlab/insidepoly.m @@ -0,0 +1,15 @@ +function [inpoly,onpoly0]=insidepoly( xi,yi,xpoly,ypoly); +inpoly=inpoly2([xi(:),yi(:)],[xpoly(:),ypoly(:)]); + +%Dummy output for onpoly +if nargout>1, + onpoly0=0*inpoly; +end +%INPOLY2 compute "points-in-polygon" queries. +% [STAT] = INPOLY2(VERT,NODE,EDGE) returns the "inside/ou- +% tside" status for a set of vertices VERT and a polygon +% {NODE,EDGE} embedded in a two-dimensional plane. General +% non-convex and multiply-connected polygonal regions can +% be handled. VERT is an N-by-2 array of XY coordinates to +% be tested. STAT is an associated N-by-1 logical array, +% with STAT(II) = TRUE if VERT(II,:) is an interior point. diff --git a/unst_msh_gen/regional/matlab/joinpslg.m b/unst_msh_gen/regional/matlab/joinpslg.m new file mode 100644 index 0000000..4bc2899 --- /dev/null +++ b/unst_msh_gen/regional/matlab/joinpslg.m @@ -0,0 +1,74 @@ +function pslg=joinpslg(pslg1,pslg2,mergeDist); +% function pslg=joinpslg(pslg1,pslg2,mergeDist); +% mergepslg::join two pslgs together. +% inputs: +% pslg1,pslg2 : pslg structures with form +% pslg.x : [n] point list of longitude +% pslg.y : [n] point list of latitude +% pslg.edges : [nedge x 2] list of edges in pslg (point adjacency) +%%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +%Keston Smith 2022 +%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +pslg1.x=pslg1.x(:)'; +pslg1.y=pslg1.y(:)'; +pslg2.x=pslg2.x(:)'; +pslg2.y=pslg2.y(:)'; + + +np1=max(max(pslg1.edges)); +%pslg1,pslg2,np1 +if ~isempty(np1), + pslg.edges=[pslg1.edges;pslg2.edges+np1]; +else + pslg.edges=[pslg1.edges;pslg2.edges]; + +end +pslg.x=[pslg1.x,pslg2.x]; +pslg.y=[pslg1.y,pslg2.y]; +holes.x=[];holes.y=[]; +if isfield(pslg1,'holes') + holes.x=[holes.x(:);pslg1.holes.x(:)]; + holes.y=[holes.y(:);pslg1.holes.y(:)]; +end +if isfield(pslg2,'holes') + holes.x=[holes.x(:);pslg2.holes.x(:)]; + holes.y=[holes.y(:);pslg2.holes.y(:)]; +end +pslg.holes=holes; + +zones.x=[];zones.y=[]; +if isfield(pslg1,'zones') + zones.x=[zones.x(:);pslg1.zones.x(:)]; + zones.y=[zones.y(:);pslg1.zones.y(:)]; +end +if isfield(pslg2,'zones') + zones.x=[zones.x(:);pslg2.zones.x(:)]; + zones.y=[zones.y(:);pslg2.zones.y(:)]; +end +pslg.zones=zones; + +if isfield(pslg1,'chains') + pslg.chains=pslg1.chains; +end + +if isfield(pslg2,'chains') + nc=length(pslg.chains); + nc2=length(pslg2.chains); + np=length(pslg.x); + for k=1:nc2 + pslg.chains(nc+k).nodes=n+pslg2.chains(k).nodes; + end +end + +%z=round((pslg.x+i*pslg.y)/MergeDist); +if nargin>2 + z=pslg.x+i*pslg.y; + n=length(pslg.x); + for k=1:n-1, + d=abs(z(k)-z(k+1:n)); + j=find(d ki(k) ); + h.e(j)=h.e(j)-1; +end diff --git a/unst_msh_gen/regional/matlab/submeshFast.m b/unst_msh_gen/regional/matlab/submeshFast.m new file mode 100644 index 0000000..09c3ab1 --- /dev/null +++ b/unst_msh_gen/regional/matlab/submeshFast.m @@ -0,0 +1,32 @@ +function h=submeshFast(g,jGoodNodes) +% +% Make the subset of unstructured mesh g consisting only of nodes +% with indexes in jGoodNodes (and only elements consisting entirely +% of nodes in jGoodNodes are preserved). +% input: +% g : FE mesh structure with fields +% g.x : longitute +% g.y : latitude +% g.z : bathymetric depth +% g.e : (ne x 3) element list +% jGoodNodes : list of nodes to keep +% +% output: +% h : portion of FE mesh structure,g spanned by nodes jGoodNodes +% + +jGoodNodes=jGoodNodes(:)';%row format + +nn=length(g.x) +[ne,three]=size(g.e) + +jGoodNodes=sort(jGoodNodes); +h.x=g.x(jGoodNodes); +h.y=g.y(jGoodNodes); +h.z=g.z(jGoodNodes); + +A=ismember(g.e,jGoodNodes); +k=find(sum(A')==3)'; +e=g.e(k,:); +[kk,M]=ismember(1:nn,jGoodNodes); +h.e=M(e); diff --git a/unst_msh_gen/regional/matlab/submeshFastEle.m b/unst_msh_gen/regional/matlab/submeshFastEle.m new file mode 100644 index 0000000..a75a722 --- /dev/null +++ b/unst_msh_gen/regional/matlab/submeshFastEle.m @@ -0,0 +1,25 @@ +function [h,k]=submeshFastEle(g,jBadNodes) +% +% Make the subset of unstructured mesh g consisting only of +% elements with no nodes in jBadNodes +% +% input: +% g : FE mesh structure with fields +% g.x : longitute +% g.y : latitude +% g.z : bathymetric depth +% g.e : (ne x 3) element list +% jBadNodes : list of nodes to throw out +% +% output: +% h : portion of FE mesh structure with no elements made entirely of +% nodes in list jBadNodes +% + +[ne,three]=size(g.e); +A=ismember(g.e,jBadNodes); +k=find(sum(A')==3)';%elements with nodes lying entirely within feature + +elist=setdiff(1:ne,k); +g.e=g.e(elist,:);%remove elements entirely within feature; +[h,k]=remove_dead_nodes(g);%remove nodes no longer in any elements diff --git a/unst_msh_gen/regional/matlab/subpslgFast.m b/unst_msh_gen/regional/matlab/subpslgFast.m new file mode 100644 index 0000000..d6c56f7 --- /dev/null +++ b/unst_msh_gen/regional/matlab/subpslgFast.m @@ -0,0 +1,35 @@ +function h=subpslgFast(g,jGoodNodes) +%function h=subpslgFast(g,jGoodNodes) +% +%Make the subset Planer Staight Line Graph(PSLG) consisting only of nodes +% with indexes in jGoodNodes (only edges consisting spanning nodes in +% jGoodNodes are preserved). +% inputs: +% g: a Planar Straight Line Graph (pslg) structure with fields +% g.x : (n x 1) x coordinates of points in pslg +% g.y : (n x 1 )y coordinates of points in pslg +% g.edges : (nedges x 2) list of edges between points +% jGoodNodes: list of points to preserve in out put h (only edges +% consisting points in jGoodNodes are preserved in h +% outputs: +% h: subset PSLG of g consisiting only of points jGoodNodes and edges +% spanning points in jGoodNodes +% +%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +%Keston Smith 2022 +%XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + +jGoodNodes=jGoodNodes(:)';%row format + +nn=length(g.x); +[ne,two]=size(g.edges); + +jGoodNodes=sort(jGoodNodes); +h.x=g.x(jGoodNodes); +h.y=g.y(jGoodNodes); + +A=ismember(g.edges,jGoodNodes); +k=find(sum(A')==2)'; +edges=g.edges(k,:); +[kk,M]=ismember(1:nn,jGoodNodes); +h.edges=M(edges); diff --git a/unst_msh_gen/regional/matlab/topo2msh.m b/unst_msh_gen/regional/matlab/topo2msh.m new file mode 100644 index 0000000..487b16b --- /dev/null +++ b/unst_msh_gen/regional/matlab/topo2msh.m @@ -0,0 +1,32 @@ +function topo=topo2msh(fl,flout) +% convert bathymetry in netcdf file fl to jigsaw m.msh format +% Retuns topo, a gridded jigsaw structure. +% inputs: +% fl : filename pointing to a netcdf bathymetry file with variables +% lon : length (nx+1) longitude of grid +% lat : length (ny+1) latitude of grid +% bed_elevation : ( nx by ny) average bathymetric depth in cell +% flout : jigsaw .msh file to write topo to. +% output: +% topo : jigsaw format girdded data representing the data in fl +% + +x=ncread(fl,'lon'); +y=ncread(fl,'lat'); +z=ncread(fl,'bed_elevation'); +x=[x(2:end)+x(1:end-1)]/2; +y=[y(2:end)+y(1:end-1)]/2; + +%z = reshape( z,length(y), length(x)); +topo.point.coord{:,1}=x(:); +topo.point.coord{:,2}=y(:); +topo.value=z; + +topo.mshID='ELLIPSOID-GRID' +topo.fileV=3; + +nargin +if nargin>2 + savemsh(flout,topo); +end + diff --git a/unst_msh_gen/regional/python/ComputeDistanceToCoast/ComputeDistanceToCoast.py b/unst_msh_gen/regional/python/ComputeDistanceToCoast/ComputeDistanceToCoast.py new file mode 100644 index 0000000..c0d91a0 --- /dev/null +++ b/unst_msh_gen/regional/python/ComputeDistanceToCoast/ComputeDistanceToCoast.py @@ -0,0 +1,143 @@ +import os +import numpy as np +import netCDF4 as nc +import jigsawpy +import geopandas as gpd +import time +from SmoothAndSubsampleCoastline import * + +MakePlots=False + +if MakePlots: + import plotly.express as px + import plotly.graph_objects as go + +#(positive integer)Subsample bathymetry coordinates to Nsubsamble +Nsubsample=2 #86 hours + +Nsubsample=5 #3.75 hours + +flout="DistToUSCoast.msh" +CoastInput=1 +if CoastInput==0: + fl="../RWPS/Data/us_coastline/tl_2023_us_coastline.shp" + #gdf = gpd.read_file("../RWPS/Data/us_coastline/tl_2023_us_coastline.shp") + dxS=5000. #(positive real)Smooth coastline to dxS meters length scale + dxI=2500. #(positive real)Interpolate smoothed coastline to dxI meters + points=SmoothAndSubsampleCoastline(fl,dxS,dxI) + lonUS=points[:,0] + latUS=points[:,1] + +if CoastInput==1: # Use precomputed smoothed & subsampled coast points from FindUSadjacentGSHHSpoints.py + fl="./USCoastPointsWithGSHHSandBanks.txt" + points=np.loadtxt(fl, delimiter=' ') + lonUS=points[1,:] + latUS=points[0,:] + +#gdf = gpd.read_file(fl) +j=np.where( np.abs(lonUS+latUS)>= 0 ) # remove NaN's +lonUS=lonUS[j] +latUS=latUS[j] + +np.savetxt('CoastPoints.txt', (lonUS, latUS), delimiter=' ') + +print("number of coastline points = " + str(lonUS.size)) +print("This number has a strong effect on run time") + +# load topo file to build distance function on the grid of +data = nc.Dataset("../RWPS/Data/RTopo_2_0_4_GEBCO_v2023_60sec_pixel.nc","r") +xlon = np.asarray(data["lon"][:]) +ylat = np.asarray(data["lat"][:]) +#elev = np.asarray(data["bed_elevation"][:]) + np.asarray(data["ice_thickness"][:]) + +if MakePlots: + fig = px.scatter(x=xlon, y=ylat) + fig.show(renderer='browser') + +xmid = 0.5 * (xlon[:-1:] + xlon[1::]) +ymid = 0.5 * (ylat[:-1:] + ylat[1::]) + +xmid=xmid[0 :: Nsubsample] +ymid=ymid[0 :: Nsubsample] + +nx=xmid.size +ny=ymid.size +npoints=lonUS.size + +D=np.zeros((ny,nx),dtype=np.single) +lat2m=np.single(110574.) +i=complex(0,1) + +t0=time.time() +D=np.zeros((ny,nx),dtype=np.single) +lat2m=np.single(110574.) +i=complex(0,1) +DLON=np.mod( np.array([xmid] * npoints) - np.array([lonUS] * nx).T, 360. ) +for k in range(0,ny-1): + print( str(k) + " of " + str(ny)+" percent:"+str(100*k/ny) ) + lon2m=np.single(111320.*np.cos(ylat[k]*np.pi/180.)) + DLAT=(ymid[k]-latUS) + DLATnx=np.array([DLAT] * nx).T + ldE=np.min ( np.abs( DLON*lon2m + i*DLATnx*lat2m ), axis=0 )#Distance to east + ldW=np.min ( np.abs( (360. - DLON)*lon2m + i*DLATnx*lat2m ), axis=0 )#Distance to west + ld=np.array([ldE,ldW]) + D[k,:]=np.min(ld,axis=0) # min of east, west distances + t1=time.time() + tps=(t1-t0)/max(k,1) + tre=tps*(ny-k) + print("estimated hours remaining: "+ str( tre /3600. ) ) + +#output to jigsaw .msh format +dist = jigsawpy.jigsaw_msh_t() +dist.mshID = "ellipsoid-grid" +dist.radii = np.full( +3, +6371.0, dtype=jigsawpy.jigsaw_msh_t.REALS_t) +dist.xgrid = xmid +dist.ygrid = ymid +dist.value = D +jigsawpy.savemsh(flout, dist) + + +#make some simple plots for sanity check +if MakePlots: + fig = px.imshow(D) + fig.show(renderer='browser') + + fig1 = go.Figure(data=go.Heatmap(z=D/1000,x=xmid,y=ymid)) + + fig1.update_layout( + title=dict(text='Distance to US coastline (km)'), + xaxis_nticks=36) + fig1.show(renderer='browser') + fig1.add_trace( + go.Scatter( + x=lonUS, + y=latUS, + mode='markers', + showlegend=False) + ) + fig1.show(renderer='browser') + +if MakePlots: + + fig2 = px.imshow(D/1000,labels=dict(x="longitude", y="latitude", color="distance"), + x=xmid, + y=ymid + ) + + fig2.update_layout( title=dict(text='Distance to US coastline (km)') ) + fig2.show(renderer='browser') + fig2.show(renderer='browser') + fig2.add_trace( + go.Scatter( + x=lonUS, + y=latUS, + mode='markers', + showlegend=False) + ) + fig2.show(renderer='browser') + + + fig = px.scatter(x=lonUS, y=latUS) + fig.add_trace(px.scatter(x=lonUS[1], y=latUS[1], mode='markers', marker=dict(color='red'), name='Second Trace')) + #fig.add_trace(go.Scatter(x=x2, y=y2, mode='markers', marker=dict(color='red'), name='Second Trace')) + fig.show(renderer='browser') diff --git a/unst_msh_gen/regional/python/ComputeDistanceToCoast/ComputeDistanceToCoastConv.py b/unst_msh_gen/regional/python/ComputeDistanceToCoast/ComputeDistanceToCoastConv.py new file mode 100644 index 0000000..7346229 --- /dev/null +++ b/unst_msh_gen/regional/python/ComputeDistanceToCoast/ComputeDistanceToCoastConv.py @@ -0,0 +1,176 @@ +import os +import numpy as np +import netCDF4 as nc +import jigsawpy +import geopandas as gpd +import time +from SmoothAndSubsampleCoastline import * + +#for 2D convolution +from scipy import signal +from scipy import datasets + +MakePlots=False + +if MakePlots: + import plotly.express as px + import plotly.graph_objects as go + +#(positive integer)Subsample bathymetry coordinates to Nsubsamble +Nsubsample=2 # ~86 hours + +Nsubsample=5 # ~3.75 hours +Nsubsample=10# ~1. hours +Nsubsample=20# ~1. hours + + +flout="DistToUSCoast.msh" +CoastInput=1 +if CoastInput==0: + fl="../RWPS/Data/us_coastline/tl_2023_us_coastline.shp" + #gdf = gpd.read_file("../RWPS/Data/us_coastline/tl_2023_us_coastline.shp") + dxS=5000. #(positive real)Smooth coastline to dxS meters length scale + dxI=2500. #(positive real)Interpolate smoothed coastline to dxI meters + points=SmoothAndSubsampleCoastline(fl,dxS,dxI) + lonUS=points[:,0] + latUS=points[:,1] + +if CoastInput==1: # Use precomputed smoothed & subsampled coast points from FindUSadjacentGSHHSpoints.py + fl="./USCoastPointsWithGSHHSandBanks.txt" + points=np.loadtxt(fl, delimiter=' ') + lonUS=points[1,:] + latUS=points[0,:] + +#gdf = gpd.read_file(fl) +j=np.where( np.abs(lonUS+latUS)>= 0 ) # remove NaN's +lonUS=lonUS[j] +latUS=latUS[j] + +np.savetxt('CoastPoints.txt', (lonUS, latUS), delimiter=' ') + +print("number of coastline points = " + str(lonUS.size)) +print("This number has a strong effect on run time") + +# load topo file to build distance function on the grid of +data = nc.Dataset("../RWPS/Data/RTopo_2_0_4_GEBCO_v2023_60sec_pixel.nc","r") +xlon = np.asarray(data["lon"][:]) +ylat = np.asarray(data["lat"][:]) +#elev = np.asarray(data["bed_elevation"][:]) + np.asarray(data["ice_thickness"][:]) + +if MakePlots: + fig = px.scatter(x=xlon, y=ylat) + fig.show(renderer='browser') + +xmid = 0.5 * (xlon[:-1:] + xlon[1::]) +ymid = 0.5 * (ylat[:-1:] + ylat[1::]) + +xmid=xmid[0 :: Nsubsample] +ymid=ymid[0 :: Nsubsample] + +nx=xmid.size +ny=ymid.size +npoints=lonUS.size + +D=np.zeros((ny,nx),dtype=np.single) +lat2m=np.single(110574.) +i=complex(0,1) + +t0=time.time() +D=np.zeros((ny,nx),dtype=np.single) +lat2m=np.single(110574.) +i=complex(0,1) +DLON=np.mod( np.array([xmid] * npoints) - np.array([lonUS] * nx).T, 360. ) +for k in range(0,ny-1): + print( str(k) + " of " + str(ny)+" percent:"+str(100*k/ny) ) + lon2m=np.single(111320.*np.cos(ylat[k]*np.pi/180.)) + DLAT=(ymid[k]-latUS) + DLATnx=np.array([DLAT] * nx).T + ldE=np.min ( np.abs( DLON*lon2m + i*DLATnx*lat2m ), axis=0 )#Distance to east + ldW=np.min ( np.abs( (360. - DLON)*lon2m + i*DLATnx*lat2m ), axis=0 )#Distance to west + ld=np.array([ldE,ldW]) + D[k,:]=np.min(ld,axis=0) # min of east, west distances + t1=time.time() + tps=(t1-t0)/max(k,1) + tre=tps*(ny-k) + print("estimated hours remaining: "+ str( tre /3600. ) ) + +#output to jigsaw .msh format +dist = jigsawpy.jigsaw_msh_t() +dist.mshID = "ellipsoid-grid" +dist.radii = np.full( +3, +6371.0, dtype=jigsawpy.jigsaw_msh_t.REALS_t) +dist.xgrid = xmid +dist.ygrid = ymid +dist.value = D +jigsawpy.savemsh(flout, dist) + +#Average output to resolution of DistToCoast +# load topo file to build distance function on the grid of +data = nc.Dataset("../RWPS/Data/RTopo_2_0_4_GEBCO_v2023_60sec_pixel.nc","r") +xlon = np.asarray(data["lon"][:]) +ylat = np.asarray(data["lat"][:]) +elev = np.asarray(data["bed_elevation"][:]) + np.asarray(data["ice_thickness"][:]) +W=np.ones((Nsubsample,Nsubsample))/(Nsubsample**2) + +#eW = signal.convolve2d(elev,W, boundary='symm', mode='same') +eW = signal.convolve2d(elev,W, mode='same') + +xmid = 0.5 * (xlon[:-1:] + xlon[1::]) +ymid = 0.5 * (ylat[:-1:] + ylat[1::]) +xmid=xmid[0 :: Nsubsample] +ymid=ymid[0 :: Nsubsample] +eW=eW[0 :: Nsubsample,0 :: Nsubsample] +topofl="TopographyFor"+flout +#output to jigsaw .msh format +topo = jigsawpy.jigsaw_msh_t() +topo.mshID = "ellipsoid-grid" +topo.radii = np.full( +3, +6371.0, dtype=jigsawpy.jigsaw_msh_t.REALS_t) +topo.xgrid = xmid +topo.ygrid = ymid +topo.value = eW +jigsawpy.savemsh(topofl, topo) + +#make some simple plots for sanity check +if MakePlots: + fig = px.imshow(D) + fig.show(renderer='browser') + + fig1 = go.Figure(data=go.Heatmap(z=D/1000,x=xmid,y=ymid)) + + fig1.update_layout( + title=dict(text='Distance to US coastline (km)'), + xaxis_nticks=36) + fig1.show(renderer='browser') + fig1.add_trace( + go.Scatter( + x=lonUS, + y=latUS, + mode='markers', + showlegend=False) + ) + fig1.show(renderer='browser') + + +if MakePlots: + + fig2 = px.imshow(D/1000,labels=dict(x="longitude", y="latitude", color="distance"), + x=xmid, + y=ymid + ) + + fig2.update_layout( title=dict(text='Distance to US coastline (km)') ) + fig2.show(renderer='browser') + fig2.show(renderer='browser') + fig2.add_trace( + go.Scatter( + x=lonUS, + y=latUS, + mode='markers', + showlegend=False) + ) + fig2.show(renderer='browser') + + + fig = px.scatter(x=lonUS, y=latUS) + fig.add_trace(px.scatter(x=lonUS[1], y=latUS[1], mode='markers', marker=dict(color='red'), name='Second Trace')) + #fig.add_trace(go.Scatter(x=x2, y=y2, mode='markers', marker=dict(color='red'), name='Second Trace')) + fig.show(renderer='browser') diff --git a/unst_msh_gen/regional/python/ComputeDistanceToCoast/FindUSadjacentGSHHSpoints.py b/unst_msh_gen/regional/python/ComputeDistanceToCoast/FindUSadjacentGSHHSpoints.py new file mode 100644 index 0000000..75d3b4c --- /dev/null +++ b/unst_msh_gen/regional/python/ComputeDistanceToCoast/FindUSadjacentGSHHSpoints.py @@ -0,0 +1,99 @@ +import numpy as np +import netCDF4 as nc +import jigsawpy +import geopandas as gpd +import time + +from SmoothAndSubsampleCoastline import * + +#compute points in the higher resolution GSHHS global coastline that are near the us coastline, +#defined in tigerline + +#https://catalog.data.gov/dataset/tiger-line-shapefile-2019-nation-u-s-coastline-national-shapefile +#https://www.soest.hawaii.edu/pwessel/gshhg/ + +flUS="../RWPS/Data/us_coastline/tl_2023_us_coastline.shp" +GlobalCoastLineFile='../RWPS/Data/GlobalCoast/GSHHS_shp/f/GSHHS_f_L1.shp' + +dxS=5000. #(positive real)Smooth coastline to dxS meters length scale +dxI=2500. #(positive real)Interpolate smoothed coastline to dxI meters +pointsUS=SmoothAndSubsampleCoastline(flUS,dxS,dxI) + +lonUS=pointsUS[:,0] +latUS=pointsUS[:,1] +#gdf = gpd.read_file(fl) +j=np.where( np.abs(lonUS+latUS)>= 0 ) # remove NaN's +lonUS=lonUS[j] +latUS=latUS[j] + +pointsGlobal=SmoothAndSubsampleCoastline(GlobalCoastLineFile,dxS,dxI) +lonGlobal=pointsGlobal[:,0] +latGlobal=pointsGlobal[:,1] + +np.savetxt('GlobalCoastPoints.txt', (latGlobal, lonGlobal), delimiter=' ') + +j=np.where( np.abs(lonUS+latUS)>= 0 ) # remove NaN's +lonUS=lonUS[j] +latUS=latUS[j] + +j=np.where( np.abs(lonGlobal+latGlobal)>= 0 ) # remove NaN's +lonGlobal=lonGlobal[j] +latGlobal=latGlobal[j] + +npGlobal=lonGlobal.size + + +t0=time.time() +D=np.zeros((npGlobal),dtype=np.single) +lat2m=np.single(110574.) +i=complex(0,1) +for k in range(0,npGlobal-1): + lon2m=np.single(111320.*np.cos(latGlobal[k]*np.pi/180.)) + DLON=np.mod( lonGlobal[k] - lonUS , 360. ) + DLAT= latGlobal[k] - latUS + ldE=np.min ( np.abs( DLON*lon2m + i*DLAT*lat2m ) )#Distance to east + ldW=np.min ( np.abs( (360. - DLON)*lon2m + i*DLAT*lat2m ) )#Distance to west + D[k]=min(ldE,ldW) # min of east, west distances + t1=time.time() + tps=(t1-t0)/max(k,1) + tre=tps*(npGlobal-k) + if np.mod(k,10000)==0: + print( str(k) + " of " + str(npGlobal)+" percent:"+str(100*k/npGlobal) ) +# print("estimated hours remaining: "+ str( tre /3600. ) ) + print("estimated minutes remaining: "+ str( tre /60. ) ) + +np.savetxt('USCoastPoints.txt', (latUS, lonUS), delimiter=' ') + +thrsh=111000. +j=np.where( D <= thrsh ) # remove NaN's +np.savetxt('USCoastPointsFromGSHHS.txt', (latGlobal[j], lonGlobal[j]), delimiter=' ') + +lonUSX=np.append(lonUS,lonGlobal[j]) +latUSX=np.append(latUS,latGlobal[j]) +np.savetxt('USCoastPointsWithGSHHS.txt', (latUSX, lonUSX), delimiter=' ') + + +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +#add points to off shore banks we want to refine here we have Georges Bank(GB) +#and banks around the Bahamas(FB) as well as Penguin bank of Molakai, HI. +#This artificially increases resolution for these features +lonFB=[ -78.6485, -78.6999, -78.3401, -76.4383, -79.9849] +latFB=[ 27.0490, 25.4556, 23.6567, 22.9114, 23.7338] +#lonFB= [ -79.9909, -78.1963, -78.3957] +#latFB= [23.7978, 26.8928, 24.1530] +lonUSX=np.append(lonUSX,lonFB) +latUSX=np.append(latUSX,latFB) +lonGB=[ -67.4977, -68.0673] +latGB=[ 41.6880, 41.1443] +#lonGB = -67.4517 +#latGB = 41.3567 +lonUSX=np.append(lonUSX,lonGB) +latUSX=np.append(latUSX,latGB) +#penguin bank HI 50m deep- probably not important in waves +lonPB =[ -157.6623, -157.5066] +latPB=[ 20.9413, 21.0486] +lonUSX=np.append(lonUSX,lonPB) +latUSX=np.append(latUSX,latPB) + +np.savetxt('USCoastPointsWithGSHHSandBanks.txt', (latUSX, lonUSX), delimiter=' ') + diff --git a/unst_msh_gen/regional/python/ComputeDistanceToCoast/ReadWriteCoast.py b/unst_msh_gen/regional/python/ComputeDistanceToCoast/ReadWriteCoast.py new file mode 100644 index 0000000..b01a95c --- /dev/null +++ b/unst_msh_gen/regional/python/ComputeDistanceToCoast/ReadWriteCoast.py @@ -0,0 +1,63 @@ + +import numpy as np +import netCDF4 as nc +import jigsawpy +import geopandas as gpd + +def ReadWriteCoast(fl): + + gdf = gpd.read_file(fl) + shp = gdf.shape + n=shp[0] + N=np.zeros((n,), dtype=int) + xss=np.array([], dtype=np.single) + yss=np.array([], dtype=np.single) + + for k in range(0, n-1): + if np.mod(k,100)==0: + print(str(k)+" "+str(n)) + seg=gdf.geometry[k] + if seg.geom_type=='LineString': + x,y = seg.coords.xy + if seg.geom_type=='Polygon': + bnd=seg.boundary + x,y = bnd.coords.xy + xp= np.array(x) + yp= np.array(y) + xss=np.append(xss,xp[:]) + yss=np.append(yss,yp[:]) + + #x,y = seg.coords.xy + #xp=numpy_array = np.array(x) + #yp=numpy_array = np.array(y) + ymp=np.mean(yp) + dx=xp[:-1:] - xp[1::] + dy=yp[:-1:] - yp[1::] + lat2m=110574. + lon2m=111320.*np.cos(ymp*np.pi/180.) + d=np.sqrt( (dx*lon2m)**2 + (dy*lat2m)**2 ) + d=np.append(0,np.cumsum(d)) + nd=d.size-1 + npoints=int(np.ceil(d[nd]/dxS)) + di=np.linspace(d[0],d[nd],npoints) + ni=di.size + xi=np.zeros((ni,), dtype=np.single) + yi=np.zeros((ni,), dtype=np.single) + for j in range(0,ni): + ks=np.where( (d-di[j])**2 < dxS**2 ) + xi[j]=np.mean(xp[ks]) + yi[j]=np.mean(yp[ks]) + xss=np.append(xss,xi[:]) + yss=np.append(yss,yi[:]) + if MakePlots: + xo=np.append(xo,xp[:]) + yo=np.append(yo,yp[:]) + + n=xss.size + point=np.zeros((n,2), dtype=np.single) + np.savetxt('CoastPoints.txt', (yss, xss), delimiter=' ') + + + point[:,0] = xss[:] + point[:,1] = yss[:] + return point diff --git a/unst_msh_gen/regional/python/ComputeDistanceToCoast/SmoothAndInterpCoastline.py b/unst_msh_gen/regional/python/ComputeDistanceToCoast/SmoothAndInterpCoastline.py new file mode 100644 index 0000000..63d0f4f --- /dev/null +++ b/unst_msh_gen/regional/python/ComputeDistanceToCoast/SmoothAndInterpCoastline.py @@ -0,0 +1,17 @@ +import geopandas as gpd +import time +from SmoothAndSubsampleCoastline import * + +import numpy as np +import netCDF4 as nc +import jigsawpy +import geopandas as gpd + + +fl='../RWPS/Data/GlobalCoast/GSHHS_shp/f/GSHHS_f_L1.shp' + #gdf = gpd.read_file("../RWPS/Data/us_coastline/tl_2023_us_coastline.shp") +dxS=10000. #(positive real)Smooth coastline to dxS meters length scale +dxI=5000. #(positive real)Interpolate smoothed coastline to dxI meters +edges=SmoothAndSubsampleCoastlineGeom(fl,dxS,dxI,100) +lonUS=points[:,0] +latUS=points[:,1] diff --git a/unst_msh_gen/regional/python/ComputeDistanceToCoast/SmoothAndSubsampleCoastline.py b/unst_msh_gen/regional/python/ComputeDistanceToCoast/SmoothAndSubsampleCoastline.py new file mode 100644 index 0000000..f191884 --- /dev/null +++ b/unst_msh_gen/regional/python/ComputeDistanceToCoast/SmoothAndSubsampleCoastline.py @@ -0,0 +1,278 @@ + +import numpy as np +import netCDF4 as nc +import jigsawpy +import geopandas as gpd + +MakePlots=False + +if MakePlots: + import plotly.express as px + import plotly.graph_objects as go + + +def SmoothAndSubsampleCoastline(fl,dxS,dxI): + + gdf = gpd.read_file(fl) + shp = gdf.shape + n=shp[0] + N=np.zeros((n,), dtype=int) + for k in range(1, n): + if np.mod(k,1000)==0: + print("Loading Coastline: "+str(k)+" of "+str(n) ) + try: + seg=gdf.geometry[k-1] + if seg.geom_type=='LineString': + x,y = seg.coords.xy + if seg.geom_type=='Polygon': + bnd=seg.boundary + x,y = bnd.coords.xy + xp= np.array(x) + yp= np.array(y) + N[k-1]=xp.size + except: + print("Error reading segment # "+str(k)) + N[k-1]=0 + + k = np.argmax(N) + m = np.max(N) + + print("smoothing "+str(n)+" coast segments. longest segment at "+str(k)+" of length "+str(m)) + #gdfS=gdf + xss=np.array([], dtype=np.single) + yss=np.array([], dtype=np.single) + + if MakePlots: + xo=np.array([], dtype=np.single) + yo=np.array([], dtype=np.single) + + for k in range(0, n-1): + if np.mod(k,100)==0: + print(str(k)+" "+str(n)) + seg=gdf.geometry[k] + if seg.geom_type=='LineString': + x,y = seg.coords.xy + if seg.geom_type=='Polygon': + bnd=seg.boundary + x,y = bnd.coords.xy + xp= np.array(x) + yp= np.array(y) + + ymp=np.mean(yp) + dx=xp[:-1:] - xp[1::] + dy=yp[:-1:] - yp[1::] + lat2m=110574. + lon2m=111320.*np.cos(ymp*np.pi/180.) + d=np.sqrt( (dx*lon2m)**2 + (dy*lat2m)**2 ) + d=np.append(0,np.cumsum(d)) + nd=d.size-1 + npoints=int(np.ceil(d[nd]/dxS)) + di=np.linspace(d[0],d[nd],npoints) + ni=di.size + xi=np.zeros((ni,), dtype=np.single) + yi=np.zeros((ni,), dtype=np.single) + for j in range(0,ni): + ks=np.where( (d-di[j])**2 < dxS**2 ) + xi[j]=np.mean(xp[ks]) + yi[j]=np.mean(yp[ks]) + xss=np.append(xss,xi[:]) + yss=np.append(yss,yi[:]) + if MakePlots: + xo=np.append(xo,xp[:]) + yo=np.append(yo,yp[:]) + + n=xss.size + point=np.zeros((n,2), dtype=np.single) + + point[:,0] = xss[:] + point[:,1] = yss[:] + if MakePlots: + + xo=xo[0 :: 100] + yo=yo[0 :: 100] + fig = go.Figure() + fig.add_trace(go.Scatter(x=xss, y=yss,mode='markers',name='smoothed'))#,color='blue')) + # go.Scatter can't handle more than ~1 mm points- so this may not work + fig.add_trace(go.Scatter(x=xo , y=yo ,mode='markers',name='origonal'))#,color='red')) + fig.show() + + return point + + +def SmoothAndSubsampleCoastlineGeom(fl,dxS,dxI,Nseg): + + gdf = gpd.read_file(fl) + shp = gdf.shape + n=shp[0] + N=np.zeros((n,), dtype=int) + #gdfS=gdf + xss=np.array([], dtype=np.single) + yss=np.array([], dtype=np.single) + + edges=np.array((-1,-1)) + edges = np.expand_dims(edges, axis=1) + + nn=0 + +# for k in range(0, n-1): + for k in range(0, Nseg): + if np.mod(k,100)==0: + print(str(k)+" "+str(n)) + seg=gdf.geometry[k] + if seg.geom_type=='LineString': + x,y = seg.coords.xy + if seg.geom_type=='Polygon': + bnd=seg.boundary + x,y = bnd.coords.xy + xp= np.array(x) + yp= np.array(y) + jj=xp.size + if jj > 2: + ymp=np.mean(yp) + dx=xp[:-1:] - xp[1::] + dy=yp[:-1:] - yp[1::] + lat2m=110574. + lon2m=111320.*np.cos(ymp*np.pi/180.) + d=np.sqrt( (dx*lon2m)**2 + (dy*lat2m)**2 ) + d=np.append(0,np.cumsum(d)) + nd=d.size-1 + npoints=int(np.ceil(d[nd]/dxS)) + di=np.linspace(d[0],d[nd],npoints) + ni=di.size + xi=np.zeros((ni,), dtype=np.single) + yi=np.zeros((ni,), dtype=np.single) + for j in range(0,ni): + ks=np.where( (d-di[j])**2 < dxS**2 ) + xi[j]=np.mean(xp[ks]) + yi[j]=np.mean(yp[ks]) + xss=np.append(xss,xi[:]) + yss=np.append(yss,yi[:]) + + mm=xi.size + l=np.arange(mm-1) + v=np.vstack((l,1+l)) + c=np.array((mm-1,0)) + cc = np.expand_dims(c, axis=1) + vc=np.hstack((v,cc) ) + edges=np.hstack((edges,vc+nn)) + nn=nn+mm + + + geom = jigsawpy.jigsaw_msh_t() + + n=xss.size + point=np.zeros((n,2), dtype=np.single) + + point[:,0] = xss[:] + point[:,1] = yss[:] + + return edges + + +def SmoothAndSubsampleCoastlineShp(flin,flout,dxS,dxI): + + gdf = gpd.read_file(flin) + gdfout=gdf + shp = gdf.shape + n=shp[0] + N=np.zeros((n,), dtype=int) + for k in range(1, n): + if np.mod(k,1000)==0: + print("Loading Coastline: "+str(k)+" of "+str(n) ) + try: + seg=gdf.geometry[k-1] + if seg.geom_type=='LineString': + x,y = seg.coords.xy + if seg.geom_type=='Polygon': + bnd=seg.boundary + x,y = bnd.coords.xy + xp= np.array(x) + yp= np.array(y) + N[k-1]=xp.size + except: + print("Error reading segment # "+str(k)) + N[k-1]=0 + + k = np.argmax(N) + m = np.max(N) + + print("smoothing "+str(n)+" coast segments. longest segment at "+str(k)+" of length "+str(m)) + #gdfS=gdf + xss=np.array([], dtype=np.single) + yss=np.array([], dtype=np.single) + + if MakePlots: + xo=np.array([], dtype=np.single) + yo=np.array([], dtype=np.single) + + for k in range(0, n-1): + if np.mod(k,100)==0: + print(str(k)+" "+str(n)) + seg=gdf.geometry[k] + if seg.geom_type=='LineString': + x,y = seg.coords.xy + if seg.geom_type=='Polygon': + bnd=seg.boundary + x,y = bnd.coords.xy + xp= np.array(x) + yp= np.array(y) + ymp=np.mean(yp) + + dx=xp[:-1:] - xp[1::] + dy=yp[:-1:] - yp[1::] + lat2m=110574. + lon2m=111320.*np.cos(ymp*np.pi/180.) + d=np.sqrt( (dx*lon2m)**2 + (dy*lat2m)**2 ) + d=np.append(0,np.cumsum(d)) + nd=d.size-1 + npoints=int(np.ceil(d[nd]/dxS)) + di=np.linspace(d[0],d[nd],npoints) + ni=di.size + xi=np.zeros((ni,), dtype=np.single) + yi=np.zeros((ni,), dtype=np.single) + for j in range(0,ni): + ks=np.where( (d-di[j])**2 < dxS**2 ) + xi[j]=np.mean(xp[ks]) + yi[j]=np.mean(yp[ks]) + + if MakePlots: + xo=xo[0 :: 100] + yo=yo[0 :: 100] + fig = go.Figure() + fig.add_trace(go.Scatter(x=xss, y=yss,mode='markers',name='smoothed'))#,color='blue')) + # go.Scatter can't handle more than ~1 mm points- so this may not work + fig.add_trace(go.Scatter(x=xo , y=yo ,mode='markers',name='origonal'))#,color='red')) + fig.show() + + return point + +def ReadWriteCoast(fl): + + gdf = gpd.read_file(fl) + shp = gdf.shape + n=shp[0] + N=np.zeros((n,), dtype=int) + xss=np.array([], dtype=np.single) + yss=np.array([], dtype=np.single) + + for k in range(0, n-1): + if np.mod(k,100)==0: + print(str(k)+" "+str(n)) + seg=gdf.geometry[k] + if seg.geom_type=='LineString': + x,y = seg.coords.xy + if seg.geom_type=='Polygon': + bnd=seg.boundary + x,y = bnd.coords.xy + xp= np.array(x) + yp= np.array(y) + xss=np.append(xss,xp[:]) + yss=np.append(yss,yp[:]) + + n=xss.size + point=np.zeros((n,2), dtype=np.single) + np.savetxt('CoastPoints.txt', (yss, xss), delimiter=' ') + + point[:,0] = xss[:] + point[:,1] = yss[:] + return point diff --git a/unst_msh_gen/regional/python/ComputeDistanceToCoast/SmoothAndSubsampleCoastlineP.py b/unst_msh_gen/regional/python/ComputeDistanceToCoast/SmoothAndSubsampleCoastlineP.py new file mode 100644 index 0000000..6355b23 --- /dev/null +++ b/unst_msh_gen/regional/python/ComputeDistanceToCoast/SmoothAndSubsampleCoastlineP.py @@ -0,0 +1,94 @@ + +import numpy as np +import netCDF4 as nc +import jigsawpy +import geopandas as gpd + +MakePlots=False +MakePlots=True + +if MakePlots: + import plotly.express as px + import plotly.graph_objects as go + + +def SmoothAndSubsampleCoastlineP(fl,dxS,dxI): + + gdf = gpd.read_file(fl) + shp = gdf.shape + n=shp[0] + N=np.zeros((n,), dtype=int) + for k in range(1, n): + if np.mod(k,1000)==0: + print("Loading Coastline: "+str(k)+" of "+str(n) ) + try: + seg=gdf.geometry[k-1] + bnd=seg.boundary + x,y = bnd.coords.xy + N[k-1]=x.size + except: + print("Error reading segment # "+str(k)) + N[k-1]=0 + + k = np.argmax(N) + m = np.max(N) + + print("smoothing "+str(n)+" coast segments. longest segment at "+str(k)+" of length "+str(m)) + #gdfS=gdf + xss=np.array([], dtype=np.single) + yss=np.array([], dtype=np.single) + + if MakePlots: + xo=np.array([], dtype=np.single) + yo=np.array([], dtype=np.single) + + for k in range(0, n-1): + if np.mod(k,100)==0: + print(str(k)+" "+str(n)) + seg=gdf.geometry[k] + bnd=seg.boundary + x,y = bnd.coords.xy + xp=numpy_array = np.array(x) + yp=numpy_array = np.array(y) + ymp=np.mean(yp) + dx=xp[:-1:] - xp[1::] + dy=yp[:-1:] - yp[1::] + lat2m=110574. + lon2m=111320.*np.cos(ymp*np.pi/180.) + d=np.sqrt( (dx*lon2m)**2 + (dy*lat2m)**2 ) + d=np.append(0,np.cumsum(d)) + nd=d.size-1 + npoints=int(np.ceil(d[nd]/dxS)) + di=np.linspace(d[0],d[nd],npoints) + ni=di.size + xi=np.zeros((ni,), dtype=np.single) + yi=np.zeros((ni,), dtype=np.single) + for j in range(0,ni): + ks=np.where( (d-di[j])**2 < dxS**2 ) + xi[j]=np.mean(xp[ks]) + yi[j]=np.mean(yp[ks]) + xss=np.append(xss,xi[:]) + yss=np.append(yss,yi[:]) + if MakePlots: + xo=np.append(xo,xp[:]) + yo=np.append(yo,yp[:]) + + n=xss.size + point=np.zeros((n,2), dtype=np.single) + + point[:,0] = xss[:] + point[:,1] = yss[:] + if MakePlots: +# fig = px.scatter(x=xss, y=yss, mode='markers', marker=dict(color='blue'), name='smoothed + subsampled coastline') +# fig = px.scatter(x=xss, y=yss) +# fig.add_trace(go.Scatter(x=xo, y=yo, mode='markers', marker=dict(color='red'), name='input coastline')) +# fig.show(renderer='browser') + xo=xo[0 :: 100] + yo=yo[0 :: 100] + fig = go.Figure() + fig.add_trace(go.Scatter(x=xss, y=yss,mode='markers',name='smoothed'))#,color='blue')) + # go.Scatter can't handle more than ~1 mm points- so this may not work + fig.add_trace(go.Scatter(x=xo , y=yo ,mode='markers',name='origonal'))#,color='red')) + fig.show() + + return point