Source code for terrainbento.boundary_handlers.generic_function_baselevel_handler

# coding: utf8
# !/usr/env/python
"""**GenericFuncBaselevelHandler** modifies elevation for not-core nodes."""


[docs]class GenericFuncBaselevelHandler(object): """Control the elevation of all nodes that are not core nodes. The **GenericFuncBaselevelHandler** controls the elevation of all nodes on the model grid with ``status != 0`` (i.e., all not-core nodes). The elevation change is defined by a generic function of the x and y position across the grid and the model time, t. Thus a user is able to use this single BaselevelHandler object to make many different uplift patterns, including uplift patterns that change as a function of model time. Through the parameter ``modify_core_nodes`` the user can determine if the core nodes should be moved in the direction (up or down) specified by the elevation change directive, or if the non-core nodes should be moved in the opposite direction. Negative values returned by the function indicate that the core nodes would be uplifted and the not-core nodes would be down-dropped. The **GenericFuncBaselevelHandler** expects that ``topographic__elevation`` is an at-node model grid field. It will modify this field as well as the field ``bedrock__elevation``, if it exists. Note that **GenericFuncBaselevelHandler** increments time at the end of the **run_one_step** method. """
[docs] def __init__( self, grid, modify_core_nodes=False, function=lambda grid, t: ( 0 * grid.x_of_node + 0 * grid.y_of_node + 0 * t ), **kwargs ): """ Parameters ---------- grid : landlab model grid modify_core_nodes : boolean, optional Flag to indicate if the core nodes or the non-core nodes will be modified. Default is False, indicating that the boundary nodes will be modified. function : function, optional Function of model grid node x position, y position and model time that defines the rate of node elevation change. This function must be a function of three variables and return an array of size number of nodes. If a constant value is desired, used **NotCoreNodeBaselevelHandler** instead. The default function is: ``lambda grid, t: (0 * grid.x_of_node + 0 * grid.y_of_node + 0 * t)`` Examples -------- Start by creating a landlab model grid and set its boundary conditions. >>> from landlab import RasterModelGrid >>> mg = RasterModelGrid((5, 5)) >>> z = mg.add_zeros("node", "topographic__elevation") >>> mg.set_closed_boundaries_at_grid_edges(bottom_is_closed=True, ... left_is_closed=True, ... right_is_closed=True, ... top_is_closed=True) >>> mg.set_watershed_boundary_condition_outlet_id( ... 0, mg.at_node["topographic__elevation"], -9999.) >>> print(z.reshape(mg.shape)) [[ 0. 0. 0. 0. 0.] [ 0. 0. 0. 0. 0.] [ 0. 0. 0. 0. 0.] [ 0. 0. 0. 0. 0.] [ 0. 0. 0. 0. 0.]] Now import the **GenericFuncBaselevelHandler** and instantiate. >>> from terrainbento.boundary_handlers import ( ... GenericFuncBaselevelHandler) >>> my_func = lambda grid, t:-(grid.x_of_node + grid.y_of_node + (0*t)) >>> bh = GenericFuncBaselevelHandler(mg, ... modify_core_nodes = False, ... function=my_func) >>> bh.run_one_step(10.0) We should expect that the boundary nodes (except for node 0) will all have lowered by ``10*(x+y)`` in which ``x`` and ``y`` are the node x and y positions. The function we provided has no time dependence. >>> print(z.reshape(mg.shape)) [[ 0. -10. -20. -30. -40.] [-10. 0. 0. 0. -50.] [-20. 0. 0. 0. -60.] [-30. 0. 0. 0. -70.] [-40. -50. -60. -70. -80.]] If we wanted instead for all of the non core nodes to change their elevation, we would set ``modify_core_nodes = True``. Next we will do an example with this option, that also includes a bedrock elevation field. >>> mg = RasterModelGrid((5, 5)) >>> z = mg.add_zeros("node", "topographic__elevation") >>> b = mg.add_zeros("node", "bedrock__elevation") >>> b -= 10. >>> mg.set_closed_boundaries_at_grid_edges(bottom_is_closed=True, ... left_is_closed=True, ... right_is_closed=True, ... top_is_closed=True) >>> mg.set_watershed_boundary_condition_outlet_id( ... 0, mg.at_node["topographic__elevation"], -9999.) >>> my_func = lambda grid, t: -(grid.x_of_node + grid.y_of_node) >>> bh = GenericFuncBaselevelHandler(mg, ... modify_core_nodes = True, ... function=my_func) >>> bh.run_one_step(10.0) >>> print(z.reshape(mg.shape)) [[ 0. 0. 0. 0. 0.] [ 0. 20. 30. 40. 0.] [ 0. 30. 40. 50. 0.] [ 0. 40. 50. 60. 0.] [ 0. 0. 0. 0. 0.]] >>> print(b.reshape(mg.shape)) [[-10. -10. -10. -10. -10.] [-10. 10. 20. 30. -10.] [-10. 20. 30. 40. -10.] [-10. 30. 40. 50. -10.] [-10. -10. -10. -10. -10.]] There is no limit to how complex a function a user can provide. The function must only take the variables ``grid``, and ``t`` and return an array that represents the desired rate of surface elevation change (dzdt) at each node. If a user wanted to use this function to implement boundary conditions that involved modifying the grid, but not necessarily modifying the elevation of core or not-core nodes, then the function could modify the grid in the desired way and then return an array of zeros of size (n_nodes,). """ self.model_time = 0.0 self.grid = grid # test the function behaves well if function.__code__.co_argcount != 2: msg = ( "GenericFuncBaselevelHandler: function must take only two " "arguments, grid and t." ) raise ValueError(msg) test_dzdt = function(self.grid, self.model_time) if hasattr(test_dzdt, "shape"): if test_dzdt.shape != self.grid.x_of_node.shape: msg = ( "GenericFuncBaselevelHandler: function must return an " "array of shape (n_nodes,)" ) raise ValueError(msg) else: msg = ( "GenericFuncBaselevelHandler: function must return an " "array of shape (n_nodes,)" ) raise ValueError(msg) self.function = function self.modify_core_nodes = modify_core_nodes self.z = self.grid.at_node["topographic__elevation"] # determine which nodes to lower # based on which are lowering, set the prefactor correctly. if self.modify_core_nodes: self.nodes_to_lower = self.grid.status_at_node == 0 self.prefactor = -1.0 else: self.nodes_to_lower = self.grid.status_at_node != 0 self.prefactor = 1.0
[docs] def run_one_step(self, step): """Run **GenericFuncBaselevelHandler** forward and update elevations. The **run_one_step** method provides a consistent interface to update the terrainbento boundary condition handlers. In the **run_one_step** routine, the **GenericFuncBaselevelHandler** will either lower the closed or raise the non-closed nodes based on inputs specified at instantiation. Note that **GenericFuncBaselevelHandler** increments time at the end of the **run_one_step** method. Parameters ---------- step : float Duration of model time to advance forward. """ self.dzdt = self.function(self.grid, self.model_time) # calculate lowering amount and subtract self.z[self.nodes_to_lower] += ( self.prefactor * self.dzdt[self.nodes_to_lower] * step ) # if bedrock__elevation exists as a field, lower it also other_fields = ["bedrock__elevation", "lithology_contact__elevation"] for of in other_fields: if of in self.grid.at_node: self.grid.at_node[of][self.nodes_to_lower] += ( self.prefactor * self.dzdt[self.nodes_to_lower] * step ) # increment model time self.model_time += step