Esempio n. 1
0
class FastscapelibContext:
    """This process takes care of proper initialization,
    update and clean-up of fastscapelib-fortran internal
    state.

    """
    shape = xs.foreign(UniformRectilinearGrid2D, 'shape')
    length = xs.foreign(UniformRectilinearGrid2D, 'length')

    elevation = xs.foreign(SurfaceTopography, 'elevation')

    context = xs.variable(
        intent='out',
        description='accessor to fastscapelib-fortran internal variables')

    def initialize(self):
        fs.fastscape_init()
        fs.fastscape_set_nx_ny(*np.flip(self.shape))
        fs.fastscape_setup()
        fs.fastscape_set_xl_yl(*np.flip(self.length))

        self.context = fs.fastscapecontext

    @xs.runtime(args='step_delta')
    def run_step(self, dt):
        # fastscapelib-fortran runtime routines use dt from context
        self.context.dt = dt

    def finalize(self):
        fs.fastscape_destroy()
Esempio n. 2
0
class FastscapelibContext:
    """This process takes care of proper initialization,
    update and clean-up of fastscapelib-fortran internal
    state.

    """
    shape = xs.foreign(UniformRectilinearGrid2D, 'shape')
    length = xs.foreign(UniformRectilinearGrid2D, 'length')
    ibc = xs.foreign(BorderBoundary, 'ibc')

    context = xs.any_object(
        description='accessor to fastscapelib-fortran internal variables')

    def initialize(self):
        fs.fastscape_init()
        fs.fastscape_set_nx_ny(*np.flip(self.shape))
        fs.fastscape_setup()
        fs.fastscape_set_xl_yl(*np.flip(self.length))

        fs.fastscape_set_bc(self.ibc)

        self.context = SerializableFastscapeContext()

    @xs.runtime(args='step_delta')
    def run_step(self, dt):
        # fastscapelib-fortran runtime routines use dt from context
        self.context["dt"] = dt

    def finalize(self):
        fs.fastscape_destroy()
Esempio n. 3
0
class TerrainDerivatives:
    """Compute, on demand, terrain derivatives such as slope or
    curvature.

    """
    shape = xs.foreign(UniformRectilinearGrid2D, 'shape')
    spacing = xs.foreign(UniformRectilinearGrid2D, 'spacing')
    elevation = xs.foreign(SurfaceTopography, 'elevation')

    slope = xs.on_demand(dims=('y', 'x'), description='terrain local slope')
    curvature = xs.on_demand(dims=('y', 'x'),
                             description='terrain local curvature')

    @slope.compute
    def _slope(self):
        slope = np.empty_like(self.elevation)
        ny, nx = self.shape
        dy, dx = self.spacing

        fs.slope(self.elevation.ravel(), slope.ravel(), nx, ny, dx, dy)

        return slope

    @curvature.compute
    def _curvature(self):
        curv = np.empty_like(self.elevation)
        ny, nx = self.shape
        dy, dx = self.spacing

        fs.curvature(self.elevation.ravel(), curv.ravel(), nx, ny, dx, dy)

        return curv
Esempio n. 4
0
class TwoBlocksUplift:
    """Set two blocks separated by a clip plane, with different
    uplift rates.

    """
    x_position = xs.variable(
        description='position of the clip plane along the x-axis')

    rate_left = xs.variable(description='uplift rate of the left block')
    rate_right = xs.variable(description='uplift rate of the right block')

    shape = xs.foreign(UniformRectilinearGrid2D, 'shape')
    x = xs.foreign(UniformRectilinearGrid2D, 'x')

    # TODO: group=['bedrock_forcing_upward', 'surface_forcing_upward']
    # see https://github.com/benbovy/xarray-simlab/issues/64
    uplift = xs.variable(dims=[(), ('y', 'x')],
                         intent='out',
                         group='any_forcing_upward',
                         description='imposed vertical uplift')

    def initialize(self):
        # align clip plane position
        self._x_idx = np.argmax(self.x > self.x_position)

    @xs.runtime(args='step_delta')
    def run_step(self, dt):
        rate = np.full(self.shape, self.rate_left)

        rate[:, self._x_idx:] = self.rate_right

        self.uplift = rate * dt
Esempio n. 5
0
class LinearDiffusion:
    """Hillslope erosion by diffusion."""

    diffusivity = xs.variable(
        dims=[(), ('y', 'x')],
        description='diffusivity (transport coefficient)'
    )
    erosion = xs.variable(
        dims=('y', 'x'),
        intent='out',
        groups='erosion'
    )

    shape = xs.foreign(UniformRectilinearGrid2D, 'shape')
    elevation = xs.foreign(SurfaceToErode, 'elevation')
    fs_context = xs.foreign(FastscapelibContext, 'context')

    def run_step(self):
        kd = np.broadcast_to(self.diffusivity, self.shape).flatten()
        self.fs_context["kd"] = kd

        # we don't use the kdsed fastscapelib-fortran feature directly
        # see class DifferentialLinearDiffusion
        self.fs_context["kdsed"] = -1.

        # bypass fastscapelib-fortran global state
        self.fs_context["h"] = self.elevation.flatten()

        fs.diffusion()

        erosion_flat = self.elevation.ravel() - self.fs_context["h"]
        self.erosion = erosion_flat.reshape(self.shape)
class OrographicDrainageDischarge(FlowAccumulator):
    """Accumulate orographic precipitation from upstream to downstream.

    For use in the context of landscape evolution modeling, ``flowacc`` values
    are converted from mm^3 h^-1 to m^3 yr^-1. For convenience, the ``discharge``
    on demand variable still returns the values in mm^3 h^-1.  
    """
    runoff = xs.foreign(OrographicPrecipitation, 'precip_rate')
    rainfall_frequency = xs.foreign(OrographicPrecipitation,
                                    'rainfall_frequency')
    discharge = xs.on_demand(
        dims=('y', 'x'),
        description='discharge from orographic precipitation',
        attrs={"units": "mm^3/h"})

    def run_step(self):
        super().run_step()

        # scale mm m^2/h to m^3/yr assuming that
        # the rainfall frequency is the number of storm of 1 hour duration per day
        self.flowacc *= 8.76 * self.rainfall_frequency

    @discharge.compute
    def _discharge(self):
        return self.flowacc / (8.76 * self.rainfall_frequency)
class ExampleProcess:
    """A process with complete interface for testing."""

    in_var = xs.variable(dims=["x", ("x", "y")], description="input variable")
    out_var = xs.variable(groups="example_group", intent="out")
    inout_var = xs.variable(intent="inout", converter=int)
    od_var = xs.on_demand()
    obj_var = xs.any_object(description="arbitrary object")

    in_foreign_var = xs.foreign(SomeProcess, "some_var")
    in_foreign_var2 = xs.foreign(AnotherProcess, "some_var")
    out_foreign_var = xs.foreign(AnotherProcess, "another_var", intent="out")
    in_foreign_od_var = xs.foreign(SomeProcess, "some_od_var")

    in_global_var = xs.global_ref("some_global_var")
    out_global_var = xs.global_ref("another_global_var", intent="out")

    group_var = xs.group("some_group")
    group_dict_var = xs.group_dict("some_group")

    other_attrib = attr.attrib(init=False, repr=False)
    other_attr = "this is not a xsimlab variable attribute"

    @od_var.compute
    def compute_od_var(self):
        return 0
Esempio n. 8
0
class UniformSedimentLayer:
    """Uniform sediment (or regolith, or soil) layer.

    This layer has uniform properties (undefined in this class) and
    generally undergo under active erosion, transport and deposition
    processes.

    """

    surf_elevation = xs.foreign(SurfaceTopography, 'elevation')
    bedrock_elevation = xs.foreign(Bedrock, 'elevation')

    thickness = xs.variable(
        dims=('y', 'x'),
        intent='out',
        description='sediment layer thickness'
    )

    @thickness.compute
    def _get_thickness(self):
        return self.surf_elevation - self.bedrock_elevation

    def initialize(self):
        self.thickness = self._get_thickness()

    def run_step(self):
        self.thickness = self._get_thickness()
Esempio n. 9
0
class VariableTwoBlockUplift:
    """compute diferent linear gradient uplift rate along the x or y axis 
    for two blocks separated by a clip plane"""

    x_position = xsimlab.variable(
        description='position of the clip plane along the x-axis')

    uplift_rate_left = xsimlab.variable(
        description='uplift rate of the left box')
    uplift_rate_right = xsimlab.variable(
        description='uplift rate of the right box')
    gradient_left = xsimlab.variable(
        description='gradient of the left box uplift')
    gradient_right = xsimlab.variable(
        description='gradient of the right box uplift')

    grid_shape = xsimlab.foreign(RasterGrid2D, 'shape')

    rate = xsimlab.foreign(BlockUplift, 'rate', intent='out')

    def run_step(self):

        mask = np.ones((self.grid_shape[0], self.grid_shape[1]))
        mask[:, 0:self.x_position] = self.uplift_rate_left
        mask[:, self.x_position:self.grid_shape[1]] = self.uplift_rate_right

        gradient = np.ones((self.grid_shape[1]))
        gradient[0:self.x_position] = np.linspace(self.gradient_left, 1,
                                                  self.x_position)
        gradient[self.x_position:self.grid_shape[1]] = np.linspace(
            self.gradient_right, 1,
            np.abs(self.grid_shape[1] - self.x_position))

        self.rate = mask * gradient
class FixedGridParams:
    spacing = xs.foreign(UniformGrid1D, "spacing", intent="out")
    length = xs.foreign(UniformGrid1D, "length", intent="out")

    def initialize(self):
        self.spacing = 0.01
        self.length = 1.0
Esempio n. 11
0
class Bedrock:
    """Update the elevation of bedrock (i.e., land and/or submarine
    basement).

    """
    elevation = xs.variable(dims=('y', 'x'),
                            intent='inout',
                            description='bedrock elevation')

    depth = xs.on_demand(dims=('y', 'x'),
                         description='bedrock depth below topographic surface')

    bedrock_motion_up = xs.foreign(TotalVerticalMotion, 'bedrock_upward')
    surface_motion_up = xs.foreign(TotalVerticalMotion, 'surface_upward')

    surface_elevation = xs.foreign(SurfaceTopography, 'elevation')

    @depth.compute
    def _depth(self):
        return self.surf_elevation - self.elevation

    def initialize(self):
        if np.any(self.elevation > self.surface_elevation):
            raise ValueError("Encountered bedrock elevation higher than "
                             "topographic surface elevation.")

    def run_step(self):
        self._elevation_next = np.minimum(
            self.elevation + self.bedrock_motion_up,
            self.surface_elevation + self.surface_motion_up)

    def finalize_step(self):
        self.elevation = self._elevation_next
class InitUFlat:
    """Flat initial profile of `u`."""

    x = xs.foreign(UniformGrid1D, "x")
    u = xs.foreign(ProfileU, "u", intent="out")

    def initialize(self):
        self.u = np.zeros_like(self.x)
Esempio n. 13
0
class InitUFlat(object):
    """Flat initial profile of `u`."""

    x = xs.foreign(UniformGrid1D, 'x')
    u = xs.foreign(ProfileU, 'u', intent='out')

    def initialize(self):
        self.u = np.zeros_like(self.x)
Esempio n. 14
0
class BareRockSurface:
    """Initialize topographic surface as a bare rock surface."""

    surf_elevation = xs.foreign(SurfaceTopography, 'elevation')
    bedrock_elevation = xs.foreign(Bedrock, 'elevation', intent='out')

    def initialize(self):
        self.bedrock_elevation = self.surf_elevation.copy()
Esempio n. 15
0
    class P2:
        var = xs.foreign(P1, "var")
        cached_var = xs.foreign(P1, "cached_var")
        view = xs.variable(dims="x", intent="out")
        cached_view = xs.variable(dims="x", intent="out")

        def run_step(self):
            self.view = self.var
            self.cached_view = self.cached_var
Esempio n. 16
0
class InitUGauss(object):
    """Initialize `u` profile using a Gaussian pulse."""

    loc = xs.variable(description='location of initial pulse')
    scale = xs.variable(description='scale of initial pulse')
    x = xs.foreign(UniformGrid1D, 'x')
    u = xs.foreign(ProfileU, 'u', intent='out')

    def initialize(self):
        self.u = np.exp(-1 / self.scale**2 * (self.x - self.loc)**2)
class InitUGauss:
    """Initialize `u` profile using a Gaussian pulse."""

    loc = xs.variable(description="location of initial pulse", static=True)
    scale = xs.variable(description="scale of initial pulse", static=True)
    x = xs.foreign(UniformGrid1D, "x")
    u = xs.foreign(ProfileU, "u", intent="out")

    def initialize(self):
        self.u = np.exp(-1 / self.scale ** 2 * (self.x - self.loc) ** 2)
Esempio n. 18
0
class FlatSurface:
    """Initialize surface topography as a flat surface at sea-level with
    random perturbations (white noise).

    """
    shape = xs.foreign(UniformRectilinearGrid2D, 'shape')
    elevation = xs.foreign(SurfaceTopography, 'elevation', intent='out')

    def initialize(self):
        self.elevation = np.random.rand(*self.shape)
Esempio n. 19
0
class StreamPowerChannel(ChannelErosion):
    """Stream-Power channel erosion."""

    k_coef = xs.variable(dims=[(), ('y', 'x')],
                         description='bedrock channel incision coefficient')
    area_exp = xs.variable(description='drainage area exponent')
    slope_exp = xs.variable(description='slope exponent')

    shape = xs.foreign(UniformRectilinearGrid2D, 'shape')
    elevation = xs.foreign(FlowRouter, 'elevation')
    receivers = xs.foreign(FlowRouter, 'receivers')
    flowacc = xs.foreign(FlowAccumulator, 'flowacc')
    fs_context = xs.foreign(FastscapelibContext, 'context')

    chi = xs.on_demand(dims=('y', 'x'),
                       description='integrated drainage area (chi)')

    def _set_g_in_context(self):
        # transport/deposition feature is exposed in subclasses
        self.fs_context.g1 = 0.
        self.fs_context.g2 = 0.

    def run_step(self):
        kf = np.broadcast_to(self.k_coef, self.shape).flatten()
        self.fs_context.kf = kf

        # we don't use kfsed fastscapelib-fortran feature directly
        self.fs_context.kfsed = -1.

        self._set_g_in_context()

        self.fs_context.m = self.area_exp
        self.fs_context.n = self.slope_exp

        # bypass fastscapelib_fortran global state
        self.fs_context.h = self.elevation.flatten()

        # TODO: https://github.com/fastscape-lem/fastscapelib-fortran/pull/25
        # this has no effect yet.
        self.fs_context.a = self.flowacc.flatten()

        if self.receivers.ndim == 1:
            fs.streampowerlawsingleflowdirection()
        else:
            fs.streampowerlaw()

        erosion_flat = self.elevation.ravel() - self.fs_context.h
        self.erosion = erosion_flat.reshape(self.shape)

    @chi.compute
    def _chi(self):
        chi_arr = np.empty_like(self.elevation, dtype='d')
        self.fs_context.copychi(chi_arr.ravel())

        return chi_arr
Esempio n. 20
0
class EscarpmentWithPertubation(Escarpment):

    y = xsimlab.foreign(UniformRectilinearGrid2D, 'y')
    grid_lenght = xsimlab.foreign(UniformRectilinearGrid2D, 'length')

    def initialize(self):
        super(EscarpmentWithPertubation, self).initialize()

        perturb = np.cos(self.x / self.grid_lenght[1] * 2. * np.pi)

        self.elevation += perturb
Esempio n. 21
0
class LocalIsostasyErosionTectonics(BaseLocalIsostasy):
    """Local isostatic effect of both erosion and tectonic forcing.

    This process makes no distinction between the density of rock and
    the density of eroded material (one single coefficient is used).

    """
    erosion = xs.foreign(TotalErosion, 'height')
    surface_upward = xs.foreign(TectonicForcing, 'surface_upward')

    def run_step(self):
        self.rebound = self.i_coef * (self.erosion - self.surface_upward)
Esempio n. 22
0
class StratigraphicHorizons:
    """Generate a fixed number of stratigraphic horizons.

    A horizon is active, i.e., it tracks the evolution of the
    land/submarine topographic surface until it is "frozen" at a given
    time. Beyond this freezing (or deactivation) time, the horizon
    will only be affected by tectonic deformation and/or erosion.

    To compute diagnostics on those horizons, you can create a
    subclass where you can add "on_demand" variables.

    """
    freeze_time = xs.variable(
        dims='horizon', description='horizon freezing (deactivation) time')

    active = xs.variable(dims='horizon',
                         intent='out',
                         description='whether the horizon is active or not')

    surf_elevation = xs.foreign(SurfaceTopography, 'elevation')
    elevation_motion = xs.foreign(TotalVerticalMotion, 'surface_upward')
    bedrock_motion = xs.foreign(TotalVerticalMotion, 'bedrock_upward')

    elevation = xs.variable(dims=('horizon', 'y', 'x'),
                            intent='out',
                            description='elevation of horizon surfaces')

    @xs.runtime(args='sim_start')
    def initialize(self, start_time):
        if np.any(self.freeze_time < start_time):
            raise ValueError("'freeze_time' value must be greater than the "
                             "time of the beginning of the simulation")

        self.elevation = np.repeat(self.surf_elevation[None, :, :],
                                   self.freeze_time.size,
                                   axis=0)

        self.active = np.full_like(self.freeze_time, True, dtype=bool)

    @xs.runtime(args='step_start')
    def run_step(self, current_time):
        self.active = current_time < self.freeze_time

    def finalize_step(self):
        elevation_next = self.surf_elevation + self.elevation_motion

        self.elevation[self.active] = elevation_next

        self.elevation[~self.active] = np.minimum(
            self.elevation[~self.active] + self.bedrock_motion, elevation_next)
class InitBasinGeom:
    """
    Give initial basin elevation field as a function of x:
    z = exp(- (x - a) / b) + c
    """
    init_br = xs.variable(dims="x", description="shift parameter", static=True)

    x = xs.foreign(UniformGrid1D, "x")
    z = xs.foreign(ProfileZ, "z", intent="out")
    h = xs.foreign(ProfileZ, "h", intent="out")

    def initialize(self):
        self.h = np.zeros(len(self.x))  #initial sediment thickness is 0
        self.z = np.zeros(len(self.x)) + self.init_br
Esempio n. 24
0
class SurfaceAfterTectonics(SurfaceToErode):
    """Used for the computation erosion processes after
    applying tectonic forcing.

    """
    topo_elevation = xs.foreign(SurfaceTopography, 'elevation')

    forced_motion = xs.foreign(TectonicForcing, 'surface_upward')

    elevation = xs.variable(dims=('y', 'x'),
                            intent='out',
                            description='surface elevation before erosion')

    def run_step(self):
        self.elevation = self.topo_elevation + self.forced_motion
Esempio n. 25
0
class LocalIsostasyErosion(BaseLocalIsostasy):
    """Local isostasic effect of erosion."""

    erosion = xs.foreign(TotalErosion, 'height')

    def run_step(self):
        self.rebound = self.i_coef * self.erosion
Esempio n. 26
0
class BorderBoundary:
    status = xs.variable(dims=[(), 'border'],
                         description='node status at borders')

    border = xs.variable(dims='border',
                         intent='out',
                         description='4-border boundaries coordinate')
    border_status = xs.variable(
        dims='border',
        intent='out',
        description='node status at the 4-border boundaries')

    fs_context = xs.foreign(FastscapelibContext, 'context')

    ibc = xs.variable(intent='out',
                      description='boundary code used by fastscapelib-fortran')

    def initialize(self):
        self.border = np.array(['left', 'right', 'top', 'bottom'])
        self.border_status = np.broadcast_to(self.status, 4)

        # convert to fastscapelib-fortran ibc code
        arr_bc = np.array(
            [1 if st == 'fixed_value' else 0 for st in self.border_status])

        # different border order
        self.ibc = sum(arr_bc * np.array([1, 100, 10, 1000]))

        self.fs_context.ibc = self.ibc
Esempio n. 27
0
class SourcePoint(object):
    """Source point for quantity `u`.

    The location of the source point is adjusted to coincide with
    the nearest node the grid.

    """
    loc = xs.variable(description='source location')
    flux = xs.variable(description='source flux')
    x = xs.foreign(UniformGrid1D, 'x')
    u_source = xs.variable(dims='x', intent='out', group='u_vars')

    @property
    def nearest_node(self):
        idx = np.abs(self.x - self.loc).argmin()
        return idx

    @property
    def source_rate(self):
        src_array = np.zeros_like(self.x)
        src_array[self.nearest_node] = self.flux
        return src_array

    def run_step(self, dt):
        self.u_source = self.source_rate * dt
Esempio n. 28
0
class LocalIsostasyTectonics(BaseLocalIsostasy):
    """Local isostasic effect of tectonic forcing."""

    bedrock_upward = xs.foreign(TectonicForcing, 'bedrock_upward')

    def run_step(self):
        self.rebound = -1. * self.i_coef * self.bedrock_upward
Esempio n. 29
0
class DifferentialStreamPowerChannel(StreamPowerChannel):
    """Stream-Power channel (differential) erosion.

    Channel incision coefficient may vary depending on whether the
    topographic surface is bare rock or covered by a soil (sediment)
    layer.

    """
    k_coef_bedrock = xs.variable(
        dims=[(), ('y', 'x')],
        description='bedrock channel incision coefficient')
    k_coef_soil = xs.variable(
        dims=[(), ('y', 'x')],
        description='soil (sediment) channel incision coefficient')

    k_coef = xs.variable(
        dims=('y', 'x'),
        intent='out',
        description='differential channel incision coefficient')

    active_layer_thickness = xs.foreign(UniformSedimentLayer, 'thickness')

    def run_step(self):
        self.k_coef = np.where(self.active_layer_thickness <= 0.,
                               self.k_coef_bedrock, self.k_coef_soil)

        super(DifferentialStreamPowerChannel, self).run_step()
class SourcePoint:
    """Source point for quantity `u`.

    The location of the source point is adjusted to coincide with
    the nearest node the grid.

    """

    loc = xs.variable(description="source location")
    flux = xs.variable(description="source flux")
    x = xs.foreign(UniformGrid1D, "x")
    u_source = xs.variable(dims="x", intent="out", groups="u_vars")

    @property
    def nearest_node(self):
        idx = np.abs(self.x - self.loc).argmin()
        return idx

    @property
    def source_rate(self):
        src_array = np.zeros_like(self.x)
        src_array[self.nearest_node] = self.flux
        return src_array

    @xs.runtime(args="step_delta")
    def run_step(self, dt):
        self.u_source = self.source_rate * dt