Coastline Evolution Model

This example explores how to use a BMI implementation using the CEM model as an example.

Interacting with the Coastline Evolution Model BMI using Python

Some magic that allows us to view images within the notebook.


In [1]:
%matplotlib inline

Import the Cem class, and instantiate it. In Python, a model with a BMI will have no arguments for its constructor. Note that although the class has been instantiated, it's not yet ready to be run. We'll get to that later!


In [2]:
import pymt.models
cem = pymt.models.Cem()


➡ models: Cem, Waves

Even though we can't run our waves model yet, we can still get some information about it. Just don't try to run it. Some things we can do with our model are get the names of the input variables.


In [3]:
cem.output_var_names


Out[3]:
('basin_outlet~coastal_center__x_coordinate',
 'basin_outlet~coastal_water_sediment~bedload__mass_flow_rate',
 'land_surface__elevation',
 'sea_water__depth',
 'basin_outlet~coastal_center__y_coordinate',
 'model__time_step')

In [4]:
cem.input_var_names


Out[4]:
('sea_surface_water_wave__azimuth_angle_of_opposite_of_phase_velocity',
 'land_surface_water_sediment~bedload__mass_flow_rate',
 'sea_surface_water_wave__period',
 'sea_surface_water_wave__height',
 'land_surface__elevation',
 'model__time_step')

We can also get information about specific variables. Here we'll look at some info about wave direction. This is the main input of the Cem model. Notice that BMI components always use CSDMS standard names. The CSDMS Standard Name for wave angle is,

"sea_surface_water_wave__azimuth_angle_of_opposite_of_phase_velocity"

Quite a mouthful, I know. With that name we can get information about that variable and the grid that it is on (it's actually not a one).


In [5]:
angle_name = 'sea_surface_water_wave__azimuth_angle_of_opposite_of_phase_velocity'

print("Data type: %s" % cem.get_var_type(angle_name))
print("Units: %s" % cem.get_var_units(angle_name))
print("Grid id: %d" % cem.get_var_grid(angle_name))
print("Number of elements in grid: %d" % cem.get_grid_number_of_nodes(0))
print("Type of grid: %s" % cem.get_grid_type(0))


Data type: float64
Units: radians
Grid id: 0
Number of elements in grid: 1
Type of grid: scalar

OK. We're finally ready to run the model. Well not quite. First we initialize the model with the BMI initialize method. Normally we would pass it a string that represents the name of an input file. For this example we'll pass None, which tells Cem to use some defaults.


In [6]:
args = cem.setup(number_of_rows=100, number_of_cols=200, grid_spacing=200.)

In [7]:
cem.initialize(*args)

Before running the model, let's set a couple input parameters. These two parameters represent the wave height and wave period of the incoming waves to the coastline.


In [8]:
import numpy as np

cem.set_value("sea_surface_water_wave__height", 2.)
cem.set_value("sea_surface_water_wave__period", 7.)
cem.set_value("sea_surface_water_wave__azimuth_angle_of_opposite_of_phase_velocity", 0. * np.pi / 180.)


Out[8]:
array([ 0.])

The main output variable for this model is water depth. In this case, the CSDMS Standard Name is much shorter:

"sea_water__depth"

First we find out which of Cem's grids contains water depth.


In [9]:
grid_id = cem.get_var_grid('sea_water__depth')

With the grid_id, we can now get information about the grid. For instance, the number of dimension and the type of grid (structured, unstructured, etc.). This grid happens to be uniform rectilinear. If you were to look at the "grid" types for wave height and period, you would see that they aren't on grids at all but instead are scalars.


In [10]:
grid_type = cem.get_grid_type(grid_id)
grid_rank = cem.get_grid_ndim(grid_id)
print('Type of grid: %s (%dD)' % (grid_type, grid_rank))


Type of grid: uniform_rectilinear (2D)

Because this grid is uniform rectilinear, it is described by a set of BMI methods that are only available for grids of this type. These methods include:

  • get_grid_shape
  • get_grid_spacing
  • get_grid_origin

In [11]:
spacing = np.empty((grid_rank, ), dtype=float)

shape = cem.get_grid_shape(grid_id)
cem.get_grid_spacing(grid_id, out=spacing)

print('The grid has %d rows and %d columns' % (shape[0], shape[1]))
print('The spacing between rows is %f and between columns is %f' % (spacing[0], spacing[1]))


The grid has 100 rows and 200 columns
The spacing between rows is 200.000000 and between columns is 200.000000

Allocate memory for the water depth grid and get the current values from cem.


In [12]:
z = np.empty(shape, dtype=float)
cem.get_value('sea_water__depth', out=z)


Out[12]:
array([[ -1. ,  -1. ,  -1. , ...,  -1. ,  -1. ,  -1. ],
       [ -1. ,  -1. ,  -1. , ...,  -1. ,  -1. ,  -1. ],
       [ -1. ,  -1. ,  -1. , ...,  -1. ,  -1. ,  -1. ],
       ..., 
       [ 22.4,  22.4,  22.4, ...,  22.4,  22.4,  22.4],
       [ 22.6,  22.6,  22.6, ...,  22.6,  22.6,  22.6],
       [ 22.8,  22.8,  22.8, ...,  22.8,  22.8,  22.8]])

Here I define a convenience function for plotting the water depth and making it look pretty. You don't need to worry too much about it's internals for this tutorial. It just saves us some typing later on.


In [13]:
def plot_coast(spacing, z):
    import matplotlib.pyplot as plt
    
    xmin, xmax = 0., z.shape[1] * spacing[0] * 1e-3
    ymin, ymax = 0., z.shape[0] * spacing[1] * 1e-3

    plt.imshow(z, extent=[xmin, xmax, ymin, ymax], origin='lower', cmap='ocean')
    plt.colorbar().ax.set_ylabel('Water Depth (m)')
    plt.xlabel('Along shore (km)')
    plt.ylabel('Cross shore (km)')

It generates plots that look like this. We begin with a flat delta (green) and a linear coastline (y = 3 km). The bathymetry drops off linearly to the top of the domain.


In [14]:
plot_coast(spacing, z)


Right now we have waves coming in but no sediment entering the ocean. To add some discharge, we need to figure out where to put it. For now we'll put it on a cell that's next to the ocean.

Allocate memory for the sediment discharge array and set the discharge at the coastal cell to some value.


In [15]:
qs = np.zeros_like(z)
qs[0, 100] = 1250

The CSDMS Standard Name for this variable is:

"land_surface_water_sediment~bedload__mass_flow_rate"

You can get an idea of the units based on the quantity part of the name. "mass_flow_rate" indicates mass per time. You can double-check this with the BMI method function get_var_units.


In [16]:
cem.get_var_units('land_surface_water_sediment~bedload__mass_flow_rate')


Out[16]:
'kg / s'

In [17]:
cem.time_step, cem.time_units, cem.time


Out[17]:
(0.2, 'd', 0.0)

Set the bedload flux and run the model.


In [18]:
for time in range(3000):
    cem.set_value('land_surface_water_sediment~bedload__mass_flow_rate', qs)
    cem.update_until(time)

cem.get_value('sea_water__depth', out=z)


Out[18]:
array([[ -1. ,  -1. ,  -1. , ...,  -1. ,  -1. ,  -1. ],
       [ -1. ,  -1. ,  -1. , ...,  -1. ,  -1. ,  -1. ],
       [ -1. ,  -1. ,  -1. , ...,  -1. ,  -1. ,  -1. ],
       ..., 
       [ 22.4,  22.4,  22.4, ...,  22.4,  22.4,  22.4],
       [ 22.6,  22.6,  22.6, ...,  22.6,  22.6,  22.6],
       [ 22.8,  22.8,  22.8, ...,  22.8,  22.8,  22.8]])

In [19]:
cem.time


Out[19]:
2999.0

In [20]:
cem.get_value('sea_water__depth', out=z)
plot_coast(spacing, z)



In [21]:
val = np.empty((5, ), dtype=float)
cem.get_value("basin_outlet~coastal_center__x_coordinate", val)
val / 100.


Out[21]:
array([  8.00000000e+01,   5.00000000e-02,   1.00000000e-01,
         1.50000000e-01,   2.00000000e-01])

Let's add another sediment source with a different flux and update the model.


In [22]:
qs[0, 150] = 1500
for time in range(3750):
    cem.set_value('land_surface_water_sediment~bedload__mass_flow_rate', qs)
    cem.update_until(time)
    
cem.get_value('sea_water__depth', out=z)


Out[22]:
array([[ -1. ,  -1. ,  -1. , ...,  -1. ,  -1. ,  -1. ],
       [ -1. ,  -1. ,  -1. , ...,  -1. ,  -1. ,  -1. ],
       [ -1. ,  -1. ,  -1. , ...,  -1. ,  -1. ,  -1. ],
       ..., 
       [ 22.4,  22.4,  22.4, ...,  22.4,  22.4,  22.4],
       [ 22.6,  22.6,  22.6, ...,  22.6,  22.6,  22.6],
       [ 22.8,  22.8,  22.8, ...,  22.8,  22.8,  22.8]])

In [23]:
plot_coast(spacing, z)


Here we shut off the sediment supply completely.


In [24]:
qs.fill(0.)
for time in range(4000):
    cem.set_value('land_surface_water_sediment~bedload__mass_flow_rate', qs)
    cem.update_until(time)
    
cem.get_value('sea_water__depth', out=z)


Out[24]:
array([[ -1. ,  -1. ,  -1. , ...,  -1. ,  -1. ,  -1. ],
       [ -1. ,  -1. ,  -1. , ...,  -1. ,  -1. ,  -1. ],
       [ -1. ,  -1. ,  -1. , ...,  -1. ,  -1. ,  -1. ],
       ..., 
       [ 22.4,  22.4,  22.4, ...,  22.4,  22.4,  22.4],
       [ 22.6,  22.6,  22.6, ...,  22.6,  22.6,  22.6],
       [ 22.8,  22.8,  22.8, ...,  22.8,  22.8,  22.8]])

In [25]:
plot_coast(spacing, z)



In [ ]: