def test_index():
    with pytest.raises(ValueError, match=r".*not accept scalar values.*"):
        xs.index(())

    # test constructor
    @attr.attrs
    class Foo:
        var = xs.index(dims="x")

    with pytest.raises(TypeError):
        # index variable not in contructor (intent='out')
        Foo(var=2)
Exemple #2
0
def gen_properties(externs, modules, modulestoconsider = None, globaldependencies = {}, propertymapping = {}):
    """ Generate the properties of the xs Process class that will run the lpyfile """
    import numpy as np
    externs = externs.difference(predefined_variables)
    if not modulestoconsider is None:
        mmodules = dict()
        for name, pnames in modules.items():
            if name in modulestoconsider:
                mmodules[name] = pnames
        modules = mmodules
    properties = {}
    properties['modules'] = modules
    for m, v in modules.items():
        properties[m] = xs.index(dims=m)
        for p in v:
            pname = m+'_'+p
            properties[pname] = propertymapping.get(pname,
                                    xs.global_ref(pname, intent='in')
                                    if pname in globaldependencies else
                                    xs.variable( dims=m, intent='out', encoding={'dtype': np.float}))
    properties['globaldependencies'] = globaldependencies
    properties['externs'] = externs
    for e in externs:
        properties[e] = xs.variable()
    return properties
Exemple #3
0
class UniformRectilinearGrid2D:
    """Create a uniform rectilinear (static) 2-dimensional grid."""

    shape = xs.variable(dims='shape_yx',
                        description='nb. of grid nodes in (y, x)',
                        static=True)
    spacing = xs.variable(dims='shape_yx',
                          description='grid node spacing in (y, x)',
                          static=True)
    origin = xs.variable(dims='shape_yx',
                         description='(y, x) coordinates of grid origin',
                         static=True)

    length = xs.variable(dims='shape_yx',
                         intent='out',
                         description='total grid length in (y, x)')
    size = xs.variable(intent='out', description='total nb. of nodes')
    area = xs.variable(intent='out', description='total grid area')
    cell_area = xs.variable(intent='out', description='fixed grid cell area')

    dx = xs.variable(intent='out', description="grid spacing in x (cols)")
    dy = xs.variable(intent='out', description="grid spacing in y (rows)")

    nx = xs.variable(intent='out', description="nb. of nodes in x (cols)")
    ny = xs.variable(intent='out', description="nb. of nodes in y (rows)")

    x = xs.index(dims='x', description='grid x coordinate')
    y = xs.index(dims='y', description='grid y coordinate')

    def _set_length_or_spacing(self):
        self.length = (self.shape - 1) * self.spacing

    def initialize(self):
        self._set_length_or_spacing()
        self.size = np.prod(self.shape)
        self.area = np.prod(self.length)
        self.cell_area = np.prod(self.spacing)

        self.dx = self.spacing[1]
        self.dy = self.spacing[0]
        self.nx = self.shape[1]
        self.ny = self.shape[0]

        self.x = np.linspace(self.origin[1], self.origin[1] + self.length[1],
                             self.shape[1])
        self.y = np.linspace(self.origin[0], self.origin[0] + self.length[0],
                             self.shape[0])
Exemple #4
0
    class Foo:
        var = xs.variable(global_name="global_var")
        idx = xs.index(dims="x", global_name="global_idx")
        obj = xs.any_object(global_name="global_obj")

        def initialize(self):
            self.idx = np.array([1, 1])
            self.obj = 2
class UniformGrid1D:
    """Create a 1-dimensional, equally spaced grid."""

    spacing = xs.variable(description="uniform spacing", static=True)
    length = xs.variable(description="total length", static=True)
    x = xs.index(dims="x")

    def initialize(self):
        self.x = np.arange(0, self.length, self.spacing)
Exemple #6
0
        class P:
            in_var = xs.variable(dims=[(), "x"])
            out_var = xs.variable(dims=[(), "x"], intent="out")
            idx_var = xs.index(dims="x")

            def initialize(self):
                self.idx_var = [0, 1]

            def run_step(self):
                self.out_var = self.in_var * 2
Exemple #7
0
        class P:
            v1 = xs.variable(dims="x",
                             intent="out",
                             encoding={"dtype": np.int32})
            v2 = xs.on_demand(dims="x", encoding={"fill_value": 0})
            v3 = xs.index(dims="x")

            @v2.compute
            def _get_v2(self):
                return [0]
class InitProfile:
    n_points = xs.variable(description="nb. of profile points",
                           converter=int,
                           static=True)

    x = xs.index(dims="x")
    u = xs.foreign(Profile, "u", intent="out")

    def initialize(self):
        self.x = np.arange(self.n_points)

        self.u = np.zeros(self.n_points)
        self.u[0] = 1.0
class UniformGrid1D:
    """Create 1D model grid with uniform spacing"""

    #grid parameters
    spacing = xs.variable(description="grid_spacing", static=True)
    length = xs.variable(description="grid total length", static=True)

    #x is an index variable, used for accessing the grid.
    x = xs.index(dims="x")

    #create the grid
    def initialize(self):
        self.x = np.arange(0, self.length, self.spacing)
        class P:
            v1 = xs.variable(dims="x",
                             intent="out",
                             encoding={"dtype": np.int32})
            v2 = xs.on_demand(dims="x", encoding={"fill_value": 0})
            v3 = xs.index(dims="x")
            v4 = xs.variable(
                dims="x",
                intent="out",
                encoding={
                    "dtype": object,
                    "object_codec": zarr.codecs.Pickle()
                },
            )

            @v2.compute
            def _get_v2(self):
                return [0]
Exemple #11
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',
        static=True
    )

    horizon = xs.index(dims='horizon', description='horizon number')

    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.horizon = np.arange(0, len(self.freeze_time))

        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
        )
Exemple #12
0
class BorderBoundary:
    """Sets boundary conditions at grid borders.

    Borders are defined in the following order:

    left, right, top, bottom

    Border status can be one of:

    - "core" (open boundary)
    - "fixed_value" (closed boundary)
    - "looped" (periodic boundary)

    "fixed_value" must be set for at least one border. This is the minimal
    constraint in order to make the numerical model solvable.

    "looped" must be symmetric, i.e., defined for (left, right)
    or (top, bottom).

    Note that currently if "core" is set for two opposed borders these
    will have periodic conditions (this comes from a current limitation in
    fastscapelib-fortran which will be solved in a next release).

    """
    status = xs.variable(dims=[(), 'border'],
                         default="fixed_value",
                         description='node status at borders',
                         static=True)

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

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

    @status.validator
    def _check_status(self, attribute, value):
        if not np.isscalar(value) and len(value) != 4:
            raise ValueError("Border status should be defined for all borders "
                             f"(left, right, top, bottom), found {value}")

        valid = ["fixed_value", "core", "looped"]
        bs = list(np.broadcast_to(value, 4))

        for s in bs:
            if s not in valid:
                raise ValueError(
                    f"Invalid border status {s!r}, must be one of {valid}")

        if "fixed_value" not in bs:
            raise ValueError(
                f"There must be at least one border with status 'fixed_value', found {bs}"
            )

        def invalid_looped(s):
            return bool(s[0] == "looped") ^ bool(s[1] == "looped")

        if invalid_looped(bs[:2]) or invalid_looped(bs[2:]):
            raise ValueError(
                f"Periodic boundary conditions must be symmetric, found {bs}")

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

        bstatus = np.array(np.broadcast_to(self.status, 4))

        # TODO: remove when solved in fastscapelib-fortran
        w_msg_common = (
            "borders have both 'core' status but periodic conditions "
            "are used due to current behavior in fastscapelib-fortran")

        if (bstatus[0] == "core" and bstatus[1] == "core"):
            w_msg = "Left and right " + w_msg_common
            warnings.warn(w_msg, UserWarning)

        if (bstatus[2] == "core" and bstatus[3] == "core"):
            w_msg = "Top and bottom " + w_msg_common
            warnings.warn(w_msg, UserWarning)

        self.border_status = bstatus

        # 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, 1000, 10]))
 class Foo:
     var = xs.index(dims="x")