Source code for terrainbento.base_class.two_lithology_erosion_model
# coding: utf8
# !/usr/env/python
"""Base class for common functions of terrainbento models with two lithologies.
The **TwoLithologyErosionModel** is a base class that contains all of
the functionality shared by the terrainbento models that have two
lithologies.
"""
import numpy as np
from terrainbento.base_class import ErosionModel
[docs]class TwoLithologyErosionModel(ErosionModel):
"""Base class for two lithology terrainbento models.
A **TwoLithologyErosionModel** inherits from
:py:class:`ErosionModel` and
provides functionality needed by all models with two lithologies.
This is a base class that handles setting up common parameters and the
contact zone elevation.
The following at-node fields must be specified in the grid:
- ``topographic__elevation``
- ``lithology_contact__elevation``
"""
_required_fields = [
"topographic__elevation",
"lithology_contact__elevation",
]
[docs] def __init__(
self,
clock,
grid,
m_sp=0.5,
n_sp=1.0,
water_erodibility_lower=0.0001,
water_erodibility_upper=0.001,
regolith_transport_parameter=0.1,
contact_zone__width=1.0,
**kwargs
):
"""
Parameters
----------
clock : terrainbento Clock instance
grid : landlab model grid instance
The grid must have all required fields.
**kwargs :
Keyword arguments to pass to :py:class:`ErosionModel`. Importantly
these arguments specify the precipitator and the runoff generator
that control the generation of surface water discharge (:math:`Q`).
Returns
-------
TwoLithologyErosionModel : object
Examples
--------
This model is a base class and is not designed to be run on its own. We
recommend that you look at the terrainbento tutorials for examples of
usage.
"""
# Call ErosionModel"s init
super().__init__(clock, grid, **kwargs)
# verify correct fields are present.
self._verify_fields(self._required_fields)
self.m = m_sp
self.n = n_sp
# Get all common parameters
self.contact_width = contact_zone__width
self.regolith_transport_parameter = regolith_transport_parameter
self.K_rock = water_erodibility_lower
self.K_till = water_erodibility_upper
# Set the erodibility values, these need to be double stated because a PrecipChanger may adjust them
self.rock_erody = self.K_rock
self.till_erody = self.K_till
self.rock_till_contact = self.grid.at_node[
"lithology_contact__elevation"
]
def _setup_rock_and_till(self):
"""Set up fields to handle for two layers with different
erodibility."""
# Create field for erodibility
self.erody = self.grid.add_zeros("node", "substrate__erodibility")
# Create array for erodibility weighting function
self.erody_wt = np.zeros(self.grid.number_of_nodes)
# Set values correctly
self._update_erodywt()
self._update_erodibility_field()
def _setup_rock_and_till_with_threshold(self):
"""Set up fields to handle for two layers with different
erodibility."""
# Create field for erodibility
self.erody = self.grid.add_zeros("node", "substrate__erodibility")
# Create field for threshold values
self.threshold = self.grid.add_zeros(
"node", "water_erosion_rule__threshold"
)
# Create array for erodibility weighting function
self.erody_wt = np.zeros(self.grid.number_of_nodes)
# set values correctly
self._update_erodywt()
self._update_erodibility_and_threshold_fields()
def _update_erodywt(self):
# Update the erodibility weighting function (this is "F")
core = self.grid.core_nodes
if self.contact_width > 0.0:
self.erody_wt[core] = 1.0 / (
1.0
+ np.exp(
-(self.z[core] - self.rock_till_contact[core])
/ self.contact_width
)
)
else:
self.erody_wt[core] = 0.0
self.erody_wt[np.where(self.z > self.rock_till_contact)[0]] = 1.0
def _update_Ks_with_precip(self):
# (if we're varying K through time, update that first)
if "PrecipChanger" in self.boundary_handlers:
erode_factor = self.boundary_handlers[
"PrecipChanger"
].get_erodibility_adjustment_factor()
self.till_erody = self.K_till * erode_factor
self.rock_erody = self.K_rock * erode_factor
def _update_erodibility_field(self):
"""Update erodibility at each node.
The erodibility at each node is a smooth function between the
rock and till erodabilities and is based on the contact zone
width and the elevation of the surface relative to contact
elevation.
"""
self._update_erodywt()
self._update_Ks_with_precip()
# Calculate the effective erodibilities using weighted averaging
self.erody[:] = (
self.erody_wt * self.till_erody
+ (1.0 - self.erody_wt) * self.rock_erody
)
def _update_erodibility_and_threshold_fields(self):
"""Update erodibility at each node.
The erodibility at each node is a smooth function between the
rock and till erodabilities and is based on the contact zone
width and the elevation of the surface relative to contact
elevation.
"""
self._update_erodywt()
self._update_Ks_with_precip()
# Calculate the effective erodibilities using weighted averaging
self.erody[:] = (
self.erody_wt * self.till_erody
+ (1.0 - self.erody_wt) * self.rock_erody
)
# Calculate the effective thresholds using weighted averaging
self.threshold[:] = (
self.erody_wt * self.till_thresh
+ (1.0 - self.erody_wt) * self.rock_thresh
)