Package quagmire

Quagmire is a Python surface process framework for building erosion and deposition models on highly parallel, decomposed structured and unstructured meshes.

Quagmire is structured into three major tiers that inherit methods and attributes from different classes:

  1. SurfaceProcessMesh
    • Calculate erosion-deposition rates
    • Landscape analysis
    • Long range flow models
  2. TopoMesh
    • Assemble downhill connectivity matrix
    • Calculate upstream area
    • Compute slope
    • Identify flat spots, low points, high points.
  3. FlatMesh
    • calculating spatial derivatives
    • identifying node neighbour relationships
    • interpolation / extrapolation
    • smoothing operators
    • importing and saving mesh information

The quagmire.surfmesh.surfmesh.SurfMesh class (1) inherits from the TopoMesh class (2), which in turn inherits from the PixMesh, TriMesh, or sTriMesh class (3) depending on the type of mesh.

Installation

Numpy and a fortran compiler, preferably gfortran, are required to install Quagmire.

  • python setup.py build
  • If you change the fortran compiler, you may have to add the flags config_fc --fcompiler=<compiler name> when setup.py is run (see docs for numpy.distutils).
  • python setup.py install

Dependencies

Running this code requires the following packages to be installed. The visualisation options are required for the notebooks and are included in the docker image.

  • Python 2.7.x and above
  • Numpy 1.9 and above
  • Scipy 0.15 and above
  • mpi4py
  • petsc4py
  • stripy
  • h5py (optional - for saving parallel data)
  • Matplotlib (optional - for visualisation)
  • lavavu (optional - for visualisation)

PETSc installation

PETSc is used extensively via the Python frontend, petsc4py. It is required that PETSc be configured and installed on your local machine prior to using Quagmire. You can use pip or petsc to install petsc4py and its dependencies with consistent versions.

[sudo] pip install numpy mpi4py
[sudo] pip install petsc petsc4py

If that fails you must compile these manually.

HDF5 installation

This is an optional installation, but it is very useful for saving data that is distributed across multiple processes. If you are compiling HDF5 from source it should be configured with the --enable-parallel flag:

CC=/usr/local/mpi/bin/mpicc ./configure --enable-parallel --enable-shared --prefix=<install-directory>
make    # build the library
make check  # verify the correctness
make install

You can then point to this install directory when you install h5py.

Usage

Quagmire is scalable in parallel. All of the python scripts in the tests subdirectory can be run in parallel, e.g.

mpirun -np 4 python stream_power.py

where the number after the -np flag specifies the number of processors.

Tutorials

Tutorials with worked examples can be found in the Notebooks subdirectory. These are Jupyter Notebooks that can be run locally. We recommend installing FFmpeg to create videos in some of the notebooks.

The topics covered in the Notebooks include:

Meshing

Triangulations and regular 2d arrays are included and have the same API.

  • Square mesh
  • Elliptical mesh
  • Mesh refinement (e.g. Lloyd's mesh improvement)

Flow algorithms

  • Single and multiple downhill pathways
  • Accumulating flow

Erosion and deposition

Operators for

  • Long-range stream flow models
  • Short-range diffusive evolution

Landscape evolution

Work in progress

  • Explicit timestepping and numerical stability
  • Landscape equilibrium metrics
  • Basement uplift

Benchmarking

Work in progress

Expand source code
# Copyright 2016-2020 Louis Moresi, Ben Mather, Romain Beucher
# 
# This file is part of Quagmire.
#
# Quagmire is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or any later version.
# 
# Quagmire is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Lesser General Public License for more details.
# 
# You should have received a copy of the GNU Lesser General Public License
# along with Quagmire.  If not, see <http://www.gnu.org/licenses/>.

"""
<img src="https://raw.githubusercontent.com/underworldcode/quagmire/dev/docs/images/AusFlow.png" style="display: block; margin: 0 auto">

**Quagmire is a Python surface process framework for building erosion and deposition models on highly parallel, decomposed structured and unstructured meshes.**

Quagmire is structured into three major tiers that inherit methods and attributes from different classes:

<img src="https://raw.githubusercontent.com/underworldcode/quagmire/dev/docs/images/quagmire-flowchart.png" style="width: 321px; float:right">

1. `SurfaceProcessMesh`
    - Calculate erosion-deposition rates
    - Landscape analysis
    - Long range flow models
2. `TopoMesh`
    - Assemble downhill connectivity matrix
    - Calculate upstream area
    - Compute slope
    - Identify flat spots, low points, high points.
3. `FlatMesh`
    - calculating spatial derivatives
    - identifying node neighbour relationships
    - interpolation / extrapolation
    - smoothing operators
    - importing and saving mesh information

The `quagmire.surfmesh.surfmesh.SurfMesh` class (1) inherits from
the `quagmire.topomesh.topomesh.TopoMesh` class (2), which in turn inherits from
the `quagmire.mesh.pixmesh.PixMesh`, `quagmire.mesh.trimesh.TriMesh`, or
`quagmire.mesh.strimesh.sTriMesh` class (3) depending on the type of mesh.


## Installation

Numpy and a fortran compiler, preferably [gfortran](https://gcc.gnu.org/wiki/GFortran), are required to install Quagmire.

- ``python setup.py build``
   - If you change the fortran compiler, you may have to add the
flags `config_fc --fcompiler=<compiler name>` when setup.py is run
(see docs for [numpy.distutils](http://docs.scipy.org/doc/numpy-dev/f2py/distutils.html)).
- ``python setup.py install``

## Dependencies

Running this code requires the following packages to be installed. The visualisation options are required for the notebooks and are included in the docker image.

- Python 2.7.x and above
- Numpy 1.9 and above
- Scipy 0.15 and above
- [mpi4py](http://pythonhosted.org/mpi4py/usrman/index.html)
- [petsc4py](https://pythonhosted.org/petsc4py/usrman/install.html)
- [stripy](https://github.com/University-of-Melbourne-Geodynamics/stripy)
- [h5py](http://docs.h5py.org/en/latest/mpi.html#building-against-parallel-hdf5) (optional - for saving parallel data)
- Matplotlib (optional - for visualisation)
- lavavu (optional - for visualisation)

### PETSc installation

PETSc is used extensively via the Python frontend, petsc4py. It is required that PETSc be configured and installed on your local machine prior to using Quagmire. You can use pip or petsc to install petsc4py and its dependencies with consistent versions.

```sh
[sudo] pip install numpy mpi4py
[sudo] pip install petsc petsc4py
```

If that fails you must compile these manually.

### HDF5 installation

This is an optional installation, but it is very useful for saving data that is distributed across multiple processes. If you are compiling HDF5 from [source](https://support.hdfgroup.org/downloads/index.html) it should be configured with the `--enable-parallel` flag:

```sh
CC=/usr/local/mpi/bin/mpicc ./configure --enable-parallel --enable-shared --prefix=<install-directory>
make    # build the library
make check  # verify the correctness
make install
```

You can then point to this install directory when you install [h5py](http://docs.h5py.org/en/latest/mpi.html#building-against-parallel-hdf5).

## Usage

Quagmire is scalable in parallel. All of the python scripts in the *tests* subdirectory can be run in parallel, e.g.

```sh
mpirun -np 4 python stream_power.py
```

where the number after the `-np` flag specifies the number of processors.

## Tutorials

Tutorials with worked examples can be found in the *Notebooks* subdirectory. These are Jupyter Notebooks that can be run locally.
We recommend installing [FFmpeg](https://ffmpeg.org/) to create videos in some of the notebooks.

The topics covered in the Notebooks include:

**Meshing**

Triangulations and regular 2d arrays are included and have the same API.

- Square mesh
- Elliptical mesh
- Mesh refinement (e.g. Lloyd's mesh improvement)

**Flow algorithms**

- Single and multiple downhill pathways
- Accumulating flow

**Erosion and deposition**

Operators for

- Long-range stream flow models
- Short-range diffusive evolution

**Landscape evolution**

_Work in progress_

- Explicit timestepping and numerical stability
- Landscape equilibrium metrics
- Basement uplift

**Benchmarking**

_Work in progress_
"""

from .mesh import PixMesh as _PixMesh
from .mesh import TriMesh as _TriMesh
from .mesh import sTriMesh as _sTriMesh
from petsc4py import PETSc as _PETSc
from .topomesh import TopoMesh as _TopoMeshClass

from . import documentation
from . import tools
from . import function
from . import scaling
from . import equation_systems

try:
    import lavavu
except:
    pass

_display = None

from mpi4py import MPI as _MPI
mpi_rank = _MPI.COMM_WORLD.rank
mpi_size = _MPI.COMM_WORLD.size


class _xvfb_runner(object):
    """
    This class will initialise the X virtual framebuffer (Xvfb).
    Xvfb is useful on headless systems. Note that xvfb will need to be
    installed, as will pyvirtualdisplay.
    This class also manages the lifetime of the virtual display driver. When
    the object is garbage collected, the driver is stopped.
    """
    def __init__(self):
        from pyvirtualdisplay import Display
        self._xvfb = Display(visible=0, size=(1600, 1200))
        self._xvfb.start()

    def __del__(self):
        if not self._xvfb is None :
            self._xvfb.stop()

import os as _os

# disable collection of data if requested
if "GLUCIFER_USE_XVFB" in _os.environ:
    from mpi4py import MPI as _MPI
    _comm = _MPI.COMM_WORLD
    if _comm.rank == 0:
        _display = _xvfb_runner()


known_basemesh_classes = {"PixMesh"  : _PixMesh, \
                          "TriMesh"  : _TriMesh, \
                          "sTriMesh" : _sTriMesh}


def _get_label(DM):
    """
    Retrieves all points in the DM that is marked with a specific label.
    e.g. "boundary", "coarse"
    """

    n = DM.getNumLabels()
    success = False

    for i in range(n):
        label = DM.getLabelName(i)
        if label in known_basemesh_classes:
            success = True
            break

    if not success:
        raise NameError("Cannot identify mesh type. DM is not valid.")

    return label



def QuagMesh(DM, downhill_neighbours=2, verbose=True, *args, **kwargs):
    """
    Instantiates a mesh in the form required for quagmire computation.
    QuagMesh identifies the type of DM and builds the necessary
    data structures for landscape processing and analysis.


    Parameters
    ----------
    DM : PETSc DM object
        Either a DMDA or DMPlex object created using the meshing
        functions within `tools.meshtools`.
    downhill_neighbours : int (default: 2)
        Number of downhill neighbours to initialise with topography.
    verbose : bool (default: True)
        Print detailed information on Quagmire routines and timings.

    Returns
    -------
    QuagMesh : object
        Inherits methods and attributes from:

        - `mesh.commonmesh.CommonMesh`
        - `mesh.pixmesh.PixMesh` (if `DM` is a regularly-spaced Cartesian grid)
        - `mesh.trimesh.TriMesh` (if `DM` is an unstructred Cartesian mesh)
        - `mesh.strimesh.sTriMesh` (if `DM` is an unstructured spherical mesh)
        - `topomesh.topomesh.TopoMesh`
    """

    BaseMeshType = _get_label(DM)

    if BaseMeshType in known_basemesh_classes:
        class QuagMeshClass(known_basemesh_classes[BaseMeshType], _TopoMeshClass):
            def __init__(self, dm, *args, **kwargs):
                if verbose:
                    print("Underlying Mesh type: {}".format(BaseMeshType))
                known_basemesh_classes[BaseMeshType].__init__(self, dm, *args, **kwargs)
                _TopoMeshClass.__init__(self, *args, **kwargs)
                # super(QuagMeshClass, self).__init__(dm, *args, **kwargs)

        return QuagMeshClass(DM, downhill_neighbours=downhill_neighbours, verbose=verbose, *args, **kwargs)

    else:
        raise TypeError("Mesh type {:s} unknown\n\
            Known mesh types: {}".format(BaseMeshType, list(known_basemesh_classes.keys())))

    return

Sub-modules

quagmire.documentation
quagmire.equation_systems
quagmire.function
quagmire.mesh

The mesh module provides 3 fundamental spatial data structures …

quagmire.scaling

The scaling module provides units and scaling capabilities.

quagmire.tools

Tools for creating and saving meshes

quagmire.topomesh

Functions

def QuagMesh(DM, downhill_neighbours=2, verbose=True, *args, **kwargs)

Instantiates a mesh in the form required for quagmire computation. QuagMesh identifies the type of DM and builds the necessary data structures for landscape processing and analysis.

Parameters

DM : PETSc DM object
Either a DMDA or DMPlex object created using the meshing functions within quagmire.tools.meshtools.
downhill_neighbours : int (default: 2)
Number of downhill neighbours to initialise with topography.
verbose : bool (default: True)
Print detailed information on Quagmire routines and timings.

Returns

QuagMesh : object

Inherits methods and attributes from:

Expand source code
def QuagMesh(DM, downhill_neighbours=2, verbose=True, *args, **kwargs):
    """
    Instantiates a mesh in the form required for quagmire computation.
    QuagMesh identifies the type of DM and builds the necessary
    data structures for landscape processing and analysis.


    Parameters
    ----------
    DM : PETSc DM object
        Either a DMDA or DMPlex object created using the meshing
        functions within `tools.meshtools`.
    downhill_neighbours : int (default: 2)
        Number of downhill neighbours to initialise with topography.
    verbose : bool (default: True)
        Print detailed information on Quagmire routines and timings.

    Returns
    -------
    QuagMesh : object
        Inherits methods and attributes from:

        - `mesh.commonmesh.CommonMesh`
        - `mesh.pixmesh.PixMesh` (if `DM` is a regularly-spaced Cartesian grid)
        - `mesh.trimesh.TriMesh` (if `DM` is an unstructred Cartesian mesh)
        - `mesh.strimesh.sTriMesh` (if `DM` is an unstructured spherical mesh)
        - `topomesh.topomesh.TopoMesh`
    """

    BaseMeshType = _get_label(DM)

    if BaseMeshType in known_basemesh_classes:
        class QuagMeshClass(known_basemesh_classes[BaseMeshType], _TopoMeshClass):
            def __init__(self, dm, *args, **kwargs):
                if verbose:
                    print("Underlying Mesh type: {}".format(BaseMeshType))
                known_basemesh_classes[BaseMeshType].__init__(self, dm, *args, **kwargs)
                _TopoMeshClass.__init__(self, *args, **kwargs)
                # super(QuagMeshClass, self).__init__(dm, *args, **kwargs)

        return QuagMeshClass(DM, downhill_neighbours=downhill_neighbours, verbose=verbose, *args, **kwargs)

    else:
        raise TypeError("Mesh type {:s} unknown\n\
            Known mesh types: {}".format(BaseMeshType, list(known_basemesh_classes.keys())))

    return