from landlab import RasterModelGrid
import numpy as np

"""
Explanation: <a href="http://landlab.github.io"><img style="float: left" src="../../landlab_header.png"></a>
Setting Boundary Conditions on the Perimeter of a Raster.
<hr>
<small>For more Landlab tutorials, click here: <a href="https://landlab.readthedocs.io/en/latest/user_guide/tutorials.html">https://landlab.readthedocs.io/en/latest/user_guide/tutorials.html</a></small>
<hr>

This tutorial illustrates how to modify the boundary conditions along the perimeter of a rectangular raster.
End of explanation
"""


mg = RasterModelGrid((4, 4))

"""
Explanation: Instantiate a grid.
End of explanation
"""


mg.status_at_node

"""
Explanation: The node boundary condition options are:

mg.BC_NODE_IS_CORE (status value = 0; all operations are performed on a mg.BC_NODE_IS_CORE)
mg.BC_NODE_IS_FIXED_VALUE (status value = 1; a boundary node with a fixed value)
mg.BC_NODE_IS_FIXED_GRADIENT (status value = 2; a boundary node with a fixed gradient)
mg.BC_NODE_IS_LOOPED (status value = 3; a boundary node that is wrap-around)
mg.BC_NODE_IS_CLOSED (status value = 4; closed boundary, or no flux can cross this node, or more accurately, can cross the faces around the node)

(Note that these options are designed for the convenience in writing Landlab applications, and  are not "automatically enforced" in internal Landlab functions. For example, if you add two node fields together, as in my_field1 + my_field2, all values will be added, not just core nodes; to take advantage of boundary coding, you would use a syntax like my_field1[grid.core_nodes] + my_field2[grid.core_nodes].)
Check the status of boundaries immediately after instantiating the grid:
End of explanation
"""


mg.imshow(mg.status_at_node)

"""
Explanation: The default conditions are for the perimeter to be fixed value (status of 1) and the interior nodes to be core (status of 0).
This is a bit easier to see graphically.
End of explanation
"""


mg.status_at_node[2] = mg.BC_NODE_IS_CLOSED
mg.imshow(mg.status_at_node, color_for_closed="blue")

"""
Explanation: Now let's choose one node on the perimeter to be closed.  
Note that imshow by default does not illustrate values for closed nodes, so we override that below and show them in blue.
End of explanation
"""


mg.set_status_at_node_on_edges(
    right=mg.BC_NODE_IS_CLOSED,
    top=mg.BC_NODE_IS_FIXED_VALUE,
    left=mg.BC_NODE_IS_CLOSED,
    bottom=mg.BC_NODE_IS_FIXED_VALUE,
)
# the same thing could be done as ...
# mg.set_status_at_node_on_edges(right=4, top=1, left=4, bottom=1)
mg.imshow(mg.status_at_node, color_for_closed="blue")

"""
Explanation: We could set the boundary condition at each node individually, or at groups of nodes (e.g. where the x_of_node value is greater than some specified value). But in many cases we just want to set the edges in one way or another. There are some functions for setting the boundary conditions around the perimeter of a raster.  (Remember that initially all of the perimeter nodes are mg.BC_NODE_IS_FIXED_VALUE by default.)
A generic way to do this is to use set_status_at_node_on_edges.
Note that this method takes the node status for whether a boundary should be closed.  The order is right, top, left, bottom.
You could send it, for example, mg.BC_NODE_IS_CLOSED, or 4, which is the value for mg.BC_NODE_IS_CLOSED status.
Below we set the right and left edges as closed and the top and bottom as fixed_value.
End of explanation
"""


mg1 = RasterModelGrid((4, 4), 1.0)
mg1.set_closed_boundaries_at_grid_edges(True, False, True, False)
mg1.imshow(mg1.status_at_node, color_for_closed="blue")

"""
Explanation: There are multiple ways to set edge boundary conditions.  If above isn't intuitive to you, keep reading.
Now let's set the right and left edges as closed boundaries using set_closed_boundaries_at_grid_edges.
Note that this method takes boolean values for whether a boundary should be closed.  The order is 
right, top, left, bottom.
Note that here we instantiate a new grid.
End of explanation
"""


mg2 = RasterModelGrid((4, 4), 1.0)
mg2.set_looped_boundaries(True, False)
mg2.imshow(mg2.status_at_node)

"""
Explanation: Now let's try setting looped boundaries using set_looped_bondaries.
Note that this method takes boolean values for whether the top and bottom (first) or right and left (second) are looped.
We will set the top and bottom to be looped (status value of 3)
End of explanation
"""


mg2.set_closed_boundaries_at_grid_edges(True, False, True, False)
mg2.imshow(mg2.status_at_node, color_for_closed="Blue")

"""
Explanation: Note that this has the right and left edges as mg.BC_NODE_IS_FIXED_VALUE (status value of 1).  
We can change those to closed if we want.
End of explanation
"""


mg3 = RasterModelGrid((4, 4), 1.0)
mg3.status_at_node[mg3.y_of_node == 0] = mg.BC_NODE_IS_FIXED_GRADIENT
mg3.status_at_node[mg3.y_of_node == 3] = mg.BC_NODE_IS_FIXED_GRADIENT
mg3.imshow(mg3.status_at_node, color_for_closed="Blue")
# there are no closed boundaries so we didn't need the color_for_closed option,
# but no problem if you accidentally include it!

"""
Explanation: Note that there are not methods for setting mg.BC_NODE_IS_FIXED_GRADIENT conditions on the boundary edges.  But we can do that.  We could use set_status_at_node_on_edges.  Below is another way to do this.
Remember that mg.BC_NODE_IS_FIXED_GRADIENT has a status value of 2.
We will set the top and bottom to be fixed gradient.
End of explanation
"""


