make_input_files -- NW_Whidbey

Notebook to make an array of fgmax points based on a RuledRectangle covering the region of interest, and also make a RuledRectangle covering these points for AMR flagging.

Also make the allow_initially_wet array for this area, if needed. This part of the notebook may need to be customized.

Make sure loc is set properly for the fgmax location, it should agree with the directory this file is in.

In the next cell use %matplotlib notebook for live interactive plots or %matplotlib inline for non-interactive plots.

In [1]:
%matplotlib inline
In [2]:
from pylab import *
In [3]:
import os,sys
from imp import reload
from clawpack.geoclaw import topotools, dtopotools
from clawpack.visclaw import colormaps

Set some values needed below:

In [4]:
loc = 'NW_Whidbey'   ## SET THIS to name to be used for this study region

save_figs = True        # make png files for figures?
In [5]:
try:
    rootdir = os.environ['WA_EMD_2019']
except:
    print('*** Need to set environment variable WA_EMD_2019')
print('Set rootdir to ', rootdir)
Set rootdir to  /Users/rjl/git/WA_EMD_2019
In [6]:
# directory for topo files:
topodir = rootdir + '/topo'

# directories where some python modules can be found:
sys.path.insert(0, rootdir + '/new_code')
sys.path.insert(0, rootdir + '/topo')
In [7]:
import fgmax_tools, fgmax_routines, kmltools  # from new_code
import region_tools, marching_front # from topo
#reload(marching_front)  # reload if debugging module
In [8]:
def savefigp(fname):
    global save_figs
    if save_figs:
        savefig(fname, bbox_inches='tight')
        print('Created ', fname)
    else:
        print('save_figs = False')

Load Google Earth image, if available

In [9]:
GEdir = rootdir + '/info/fgmax_regions'
try:
    GEfile = GEdir + '/%s_bbox_GE.png' % loc
    GEmap = imread(GEfile)
    GEextent_file = GEdir + '/%s_bbox_GE_extent.txt' % loc
    GEextent_tokens = open(GEextent_file,'r').read().strip().split(',')
    GEextent = [float(s) for s in GEextent_tokens]
    print('GE extent: ', GEextent)
except:
    print('Problem reading Google Earth image file or extent')
    GEmap = None
    
if GEmap is not None:
    figure(figsize=(8,8))
    imshow(GEmap, extent=GEextent)
    gca().set_aspect(1./cos(48*pi/180.))
    ticklabel_format(useOffset=False)
    xticks(rotation=20);
GE extent:  [-122.81037, -122.628611, 48.18963, 48.427407]
In [10]:
zmin = -60.
zmax = 40.

land_cmap = colormaps.make_colormap({ 0.0:[0.1,0.4,0.0],
                                     0.25:[0.0,1.0,0.0],
                                      0.5:[0.8,1.0,0.5],
                                      1.0:[0.8,0.5,0.2]})

sea_cmap = colormaps.make_colormap({ 0.0:[0,0,1], 1.:[.8,.8,1]})

cmap, norm = colormaps.add_colormaps((land_cmap, sea_cmap),
                                     data_limits=(zmin,zmax),
                                     data_break=0.)
                                     
sea_cmap_dry = colormaps.make_colormap({ 0.0:[1.0,0.7,0.7], 1.:[1.0,0.7,0.7]})
cmap_dry, norm_dry = colormaps.add_colormaps((land_cmap, sea_cmap_dry),
                                     data_limits=(zmin,zmax),
                                     data_break=0.)
In [11]:
PTPSm = topotools.read_netcdf(topodir + '/topofiles/PTPS_merged_13s.nc')

Ruled rectangle used as boundary polygon for fgmax points

We will select fgmax points that lie within this polygon, with other conditions also imposed later.

In [12]:
slu = \
    array([ [48.190, -122.760, -122.684],
            [48.221, -122.810, -122.755],
            [48.367, -122.720, -122.632],
            [48.427, -122.686, -122.629]])

fg_poly = region_tools.RuledRectangle(slu=slu)

#fg_poly.s = slu[:,0]
#fg_poly.lower = slu[:,1]
#fg_poly.upper = slu[:,2]
fg_poly.ixy = 2
fg_poly.method = 1

# Cropped topo file:
#extent = [-122.54, -122.41, 47.9, 48.04] # custom extent if desired
x1,x2,y1,y2 = fg_poly.bounding_box()
padding = 5./(3*3600.)  # add a few grid cells around bounding box
extent = [x1-padding, x2+padding, y1-padding, y2+padding]
print('Cropping PTPSm topo to: ', extent)
topo = PTPSm.crop(extent)
extent = topo.extent
print('New extent of topo: ', extent)
fg_poly.write('fg_poly_%s.slu' % loc)
Cropping PTPSm topo to:  [-122.81046296296296, -122.62853703703705, 48.189537037037034, 48.42746296296296]
New extent of topo:  (-122.810370380725, -122.62861112655501, 48.18962962544, 48.427407407394206)
In [13]:
def plot_topo(Z, show_cb=True):
    #figure(figsize=(8,8))
    pc = pcolormesh(topo.X, topo.Y, Z, cmap=cmap, norm=norm)
    if show_cb:
        cb = colorbar(pc)
        cb.set_label('meters')
    axis(extent)
    gca().set_aspect(1./cos(48*pi/180.))
    ticklabel_format(useOffset=False)
    xticks(rotation=20)
In [14]:
figure(figsize=(8,8))
plot_topo(topo.Z)
xpoly,ypoly = fg_poly.vertices()
plot(xpoly,ypoly,'w')
title('Topography and fg_poly')
savefigp('fg_poly_%s.png' % loc)
Created  fg_poly_NW_Whidbey.png

Make kml files

Make a kml file showing the polygon used above, and one showing the bounding box as a rectangle. The latter is useful for capturing a Google Earth image of the fgmax region in order to plot or topography on top of the image.

In [15]:
kmltools.poly2kml((xpoly,ypoly), name='%s_fgmax_poly' % loc, fname='%s_fgmax_poly.kml' % loc)
kmltools.box2kml(extent, name='%s_fgmax_bbox' % loc, fname='%s_fgmax_bbox.kml' % loc)
Polygon:     -122.760000   48.190000
             -122.810000   48.221000
             -122.720000   48.367000
             -122.686000   48.427000
             -122.629000   48.427000
             -122.632000   48.367000
             -122.755000   48.221000
             -122.684000   48.190000
             -122.760000   48.190000
Created  NW_Whidbey_fgmax_poly.kml
Box:   -122.810370  -122.628611   48.189630   48.427407
Created  NW_Whidbey_fgmax_bbox.kml

Select fgmax points within this polygon

First use the criterion that points are selected everywhere in the bounding box rectangle, as specified by the extent of the topo array, and if one of these conditions holds:

  • the point is offshore at any depth of water, i.e., Z < 0,
  • the point lies within onshore_buffer grid points of the shoreline, or
  • the topography satisfies B < onshore_Z, and is connected to the shore by a chain of grid points also satisfying this condition.

The latter requirement avoids selecting points in a local depression that are separated from the coast by high ground that won't be overtopped.

This is accomplished by a marching front algorithm defined in rootdir + '/topo/marching_front.py'.

In [16]:
# mask the points not in fg_poly:
mask_out = fg_poly.mask_outside(topo.X, topo.Y)
#mask_fg = logical_or(mask_out, mask_near)
In [17]:
fgmax_pts_chosen = marching_front.select_by_flooding(topo.Z, mask=mask_out, 
                                                     prev_pts_chosen=None, 
                                                     Z1=0, Z2=1e6, max_iters=10)
Selecting points with Z1 = 0, Z2 = 1e+06, max_iters=10
Done after 10 iterations with 1232962 points chosen
In [18]:
fgmax_pts_chosen = marching_front.select_by_flooding(topo.Z, mask=mask_out, 
                                                     prev_pts_chosen=fgmax_pts_chosen, 
                                                     Z1=0, Z2=15., max_iters=None) 
Selecting points with Z1 = 0, Z2 = 15, max_iters=5045516
Done after 319 iterations with 1407122 points chosen
In [19]:
# mask the points not selected above:
mask_fg = logical_not(fgmax_pts_chosen)

# topography as masked array:
Z_fg = ma.masked_array(topo.Z, mask_fg)

print('Selected %i fgmax points' % Z_fg.count())
Selected 1407122 fgmax points
In [20]:
figure(figsize=(8,8))
plot_topo(Z_fg)
plot(xpoly,ypoly,'r')
title('%i fgmax points in specified polygon' % Z_fg.count())
savefigp('fgmax_points_%s.png' % loc)
Created  fgmax_points_NW_Whidbey.png

Write out the masked array indicating fgmax points

This is written in the style of a topo_type=3 topography file, with a header followed point values at all points on a uniform grid. The values are simply the integer 1 for points that should be used as fgmax points and 0 for other points. Note that format %1i is used for compactness.

In [21]:
fname_fgmax_mask = 'fgmax_pts_topostyle_%s.data' % loc
topo_fgmax_mask = topotools.Topography()
topo_fgmax_mask._x = topo.x
topo_fgmax_mask._y = topo.y     
topo_fgmax_mask._Z = where(Z_fg.mask, 0, 1)
topo_fgmax_mask.generate_2d_coordinates()

topo_fgmax_mask.write(fname_fgmax_mask, topo_type=3, Z_format='%1i')
print('Created %s' % fname_fgmax_mask)
Created fgmax_pts_topostyle_NW_Whidbey.data

Write out topo file with DEM Z values

Don't need this since we'll put topo in netCDF file.

In [22]:
if 0:
    fname_fgmax_topo = 'fgmax_topo_%s.nc' % loc
    topo.write(fname_fgmax_topo, topo_type=4)
    print('Created %s' % fname_fgmax_topo)

Make RuledRectangle for AMR flagging to the finest level

We wish to force GeoClaw to refine the region around the fgmax points to the finest level (generally 1/3" to agree with the resolution of the fgmax points). Rather than trying to specify rectangular regions that cover the fgmax points efficiently, we can use a RuledRectangle.

We can make a RuledRectangle that includes all the fgmax points in two different ways, either with s = x so the rules go from ylower to yupper on each column of grid points, or with s = y so that the rules go from xlower to xupper along each row of grid points.

Which approach is best depends on the geometry of the fgmax points. We would like the RuledRectangle to cover as few points as possible that are not fgmax points.

Here we compute and display both options.

In [23]:
x = topo.x
y = topo.y

# Ruled rectangle with s = x:

rr1 = region_tools.RuledRectangle()
rr1.ixy = 1 
s = []
lower = []
upper = []
for i in range(len(x)):
    if fgmax_pts_chosen[:,i].sum() > 0:
        j = where(fgmax_pts_chosen[:,i]==1)[0]
        j1 = j.min()
        j2 = j.max()
        s.append(x[i])
        lower.append(y[j1])
        upper.append(y[j2])
        
rr1.s = array(s)
rr1.lower = array(lower)
rr1.upper = array(upper)
rr1.method = 0
rr1.ds = s[1] - s[0]

rr1_npts = int(ceil(sum(rr1.upper - rr1.lower) *3*3600))
print('RuledRectangle rr1 covers %s grid points' % rr1_npts)

# Ruled rectangle with s = y:

rr2 = region_tools.RuledRectangle()
rr2.ixy = 2
s = []
lower = []
upper = []
for j in range(len(y)):
    if fgmax_pts_chosen[j,:].sum() > 0:
        i = where(fgmax_pts_chosen[j,:]==1)[0]
        i1 = i.min()
        i2 = i.max()
        s.append(y[j])
        lower.append(x[i1])
        upper.append(x[i2])
               
rr2.s = array(s)
rr2.lower = array(lower)
rr2.upper = array(upper)
rr2.method = 0
rr2.ds = s[1] - s[0]

rr2_npts = int(ceil(sum(rr2.upper - rr2.lower)*3*3600))
print('RuledRectangle rr2 covers %s grid points' % rr2_npts)


# Create files for AMR flag regions:

fname1 = 'RuledRectangle_%s_ixy1.data' % loc
rr1.write(fname1)
print('Created ',fname1)

fname2 = 'RuledRectangle_%s_ixy2.data' % loc
rr2.write(fname2)
print('Created ',fname2)
RuledRectangle rr1 covers 2011367 grid points
RuledRectangle rr2 covers 1461370 grid points
Created  RuledRectangle_NW_Whidbey_ixy1.data
Created  RuledRectangle_NW_Whidbey_ixy2.data
In [24]:
# Plot comparison of two approaches:

figure(figsize=(10,8))
subplot(121)
plot_topo(Z_fg, show_cb=False)
x2,y2 = rr1.vertices()
plot(x2,y2,'r')
title(fname1 + '\n%i points covered' % rr1_npts)

subplot(122)
plot_topo(Z_fg, show_cb=False)
x2,y2 = rr2.vertices()
plot(x2,y2,'r')
title(fname2 + '\n%i points covered' % rr2_npts)

savefigp('RuledRectangles_%s.png' % loc)
Created  RuledRectangles_NW_Whidbey.png

Deal with dry land below MHW

If this region does not have any land below MHW, can simply set:

In [25]:
allow_wet_init = None

For this fgmax region, the only areas behind dikes are initially lakes, so ok if GeoClaw initializes as wet.

In [27]:
figure(figsize=(10,8))
subplot(121)
#pc = pcolormesh(topo.X, topo.Y, Z_dry, cmap=cmap_dry, norm=norm_dry)
pc = pcolormesh(topo.X, topo.Y, Z_fg, cmap=cmap, norm=norm)
#cb = colorbar(pc)
#cb.set_label('meters')
gca().set_aspect(1./cos(48*pi/180.))
ticklabel_format(useOffset=False)
xticks(rotation=20)
title('%s: %i fgmax points\n(no dry regions below MHW)' % (loc, Z_fg.count()))


subplot(122)
if GEmap is not None:
    imshow(GEmap, extent=GEextent)
    gca().set_aspect(1./cos(48*pi/180.))
    ticklabel_format(useOffset=False)
    xticks(rotation=20);
else:
    print('Could not make plot with image')
    
savefigp('allow_wet_init_%s.png' % loc)
Created  allow_wet_init_NW_Whidbey.png

Make figure for viewing as kml

In [28]:
Z_dry = ma.masked_where(logical_or(topo.Z<0, mask_fg), topo.Z)
Z_dry_above0 = Z_dry
fig,ax,png_extent,kml_dpi = kmltools.pcolorcells_for_kml(topo.x, topo.y, Z_dry_above0,
                                                 png_filename='dry_above0_for_kml.png',
                                                 dpc=2, cmap=cmap_dry, norm=norm_dry)
Using kml_dpi = 343,figure size 11.451895 by 14.979592 inches
Figure has 1964 by 2569 grid cells of uniform color
Dots per cell in x: 2.000000,  in y: 2.000000
       These should be integers
Created  dry_above0_for_kml.png
In [29]:
mask_dry = logical_not(Z_dry.mask)
mm = logical_or(mask_fg, mask_dry)
Z_wet = ma.masked_array(topo.Z, mm)
fig,ax,png_extent,kml_dpi = kmltools.pcolorcells_for_kml(topo.x, topo.y, Z_wet,
                                                 png_filename='wet_for_kml.png',
                                                 dpc=2, cmap=cmap, norm=norm)
Using kml_dpi = 343,figure size 11.451895 by 14.979592 inches
Figure has 1964 by 2569 grid cells of uniform color
Dots per cell in x: 2.000000,  in y: 2.000000
       These should be integers
Created  wet_for_kml.png
In [30]:
kmltools.png2kml(topo.extent, png_files=['dry_above0_for_kml.png',
                                         'wet_for_kml.png'],
                 png_names=['dry above MHW','allow wet'], 
                 name='fgmax_points_%s' % loc, 
                 fname='fgmax_points_%s.kmz' % loc)
Extent:   -122.810370  -122.628611   48.189630   48.427407
Created  fgmax_points_NW_Whidbey.kmz

Create netCDF file with topo, fgmax points, wet mask:

First crop the arrays down to the bounding box of the fgmax points.

In [31]:
print('original shape of X,Y,Z: ', Z_fg.shape)
iindex = array([i for i in range(Z_fg.shape[0]) \
                if ma.notmasked_edges(Z_fg[i,:]) is not None])
jindex = array([j for j in range(Z_fg.shape[1]) \
                if ma.notmasked_edges(Z_fg[:,j]) is not None])
i1 = iindex.min(); i2 = iindex.max()
j1 = jindex.min(); j2 = jindex.max()
print('First and last iindex: %7i, %7i' % (i1,i2))
print('First and last jindex: %7i, %7i' % (j1,j2))
Zcrop = Z_fg[i1:i2+1, j1:j2+1]
print('new shape of X,Y,Z: ', Zcrop.shape)
Xcrop = topo.X[i1:i2+1, j1:j2+1]
Ycrop = topo.Y[i1:i2+1, j1:j2+1]
original shape of X,Y,Z:  (2569, 1964)
First and last iindex:       5,    2563
First and last jindex:       5,    1952
new shape of X,Y,Z:  (2559, 1948)

Plot to make sure it looks right

In [32]:
figure(figsize=(8,8))
pc = pcolormesh(Xcrop, Ycrop, Zcrop, cmap=cmap, norm=norm)
gca().set_aspect(1./cos(48*pi/180.))
ticklabel_format(useOffset=False)
xticks(rotation=20);
#axis(extent)
#plot(xpoly,ypoly,'r');
In [33]:
fgm = fgmax_tools.FGmaxMaskedGrid()
fgm.id = loc
fgm.X = Xcrop
fgm.Y = Ycrop
fgm.Z = Zcrop
fgm.fgmax_point = where(Zcrop.mask, 0, 1)
print('Created fgm object')
print('Check that this has the expected number of fgmax points: ',\
      fgm.fgmax_point.sum())
if allow_wet_init is not None:
    fgm.allow_wet_init = allow_wet_init_topo.Z[i1:i2+1, j1:j2+1]
Created fgm object
Check that this has the expected number of fgmax points:  1407122
In [34]:
fname_nc = '%s_input.nc' % loc
fgmax_routines.make_nc_input(fname_nc, fgm, force=True)
print('Created ',fname_nc)
fgm.allow_wet_init is None, not adding
Created NW_Whidbey_input.nc
History:   Created with input data Fri Sep 27 15:40:19 2019 in /Users/rjl/git/WA_EMD_2019/Runs/NW_Whidbey;  
Created  NW_Whidbey_input.nc

Save resulting input files

Save the *.data, *.nc (and optionally *.png and *.kml) files created by this notebook to a (new) directory input_files if you don't already have them saved in this directory, since this is where GeoClaw will look for them. Back up an old version first if you want to, for safety.