.. _hydromt_python:

Using HydroMT in Python
=======================

As HydroMT's architecture is modular, it is possible to use HydroMT as a python library
without using the command line interface (CLI). Using the libary, you have more of
HydroMTs functionalities at your disposal. This can be useful if you want to for
example:

- build / update / clip models, check configurations or export data in Python instead of
  the CLI
- analyse model inputs or results
- analyse and compare input data without connecting to a specific model
- process input data for another reason than building a model
- create your own HydroMT plugin

So first, let's go deeper into the API of HydroMT. You have all available functions
and their documentation in the `API reference <../api.rst>`_.

HydroMT is here to read and harmonize **input data**, and to process it via its
**methods and workflows** in order to prepare ready to run **models**. So HydroMT's
methods are organized around these main objects.

Input data
----------

.. currentmodule:: hydromt.data_catalog

Most classes around the finding, reading and transforming input data have
implementations for the five different data_types in HydroMT.
The main objects to work with input data are:

* The :class:`~data_catalog.DataCatalog` is the most high-level class and leverages the
    next few classes to find, read and transform the data coming from its configuration.
    The methods used to do this are called  ``get_<data_type>``, for example
    :func:`~data_catalog.DataCatalog.get_rasterdataset`.
* The :class:`~uri_resolvers.uri_resolver.URIResolver` is responsible for finding the
    data based on a single uri. This class is generic for all data_types. An
    implementation that finds data based on naming conventions is the
    :class:`~uri_resolvers.convention_resolver.ConventionResolver`.
* The :class:`Driver <drivers.base_driver.BaseDriver>` has different subclasses based on
    the data_type, for example
    :class:`~drivers.raster.raster_dataset_driver.RasterDatasetDriver`, which then has
    different implementations, for example a driver for reading raster data using
    rasterio: :class:`~drivers.raster.rasterio_driver.RasterioDriver`. which reads
    raster data.
* The :class:`DataAdapter <adapters.data_adapter_base.DataAdapterBase>` has subclasses
    that transform data, like renaming, reprojecting etc. These subclasses are for
    example: :class:`~adapters.rasterdataset.RasterDatasetAdapter`.

So let's say you would like to read data in HydroMT, you can do this by creating a
DataCatalog instance and using the ``get_<data_type>`` methods to read the data.
This is a short example:

.. code-block:: python

    import hydromt
    # create a data catalog from a local data_catalog file
    cat = hydromt.DataCatalog("data_catalog.yml")
    # read a raster dataset ("dem" source in the data catalog)
    dem = cat.get_rasterdataset("dem")
    # read a vector dataset ("catchments" source in the data catalog)
    catchments = cat.get_geodataframe("catchments")
    # read a geodataset with some time and space slicing
    qobs = cat.get_geodataset(
      "qobs",
      time_range = ("2000-01-01", "2010-12-31"),
      bbox = [5.0, 50.0, 6.0, 51.0]
    )

You can find more detailed examples on using the DataCatalog and DataAdapter in Python in:

* `Reading raster data <../_examples/reading_raster_data.ipynb>`_
* `Reading vector data <../_examples/reading_vector_data.ipynb>`_
* `Reading geospatial point time-series data <../_examples/reading_point_data.ipynb>`_
* `Preparing a data catalog <../_examples/prep_data_catalog.ipynb>`_
* `Exporting data <../_examples/export_data.ipynb>`_

.. _xarray_accessors:

Xarray Accessors
----------------
Some powerful functionality that HydroMT uses is exposed in the ``gis`` module. In this
module `xarray accessors
<https://docs.xarray.dev/en/stable/internals/extending-xarray.html>`_ are located. These
allow for powerful new methods on top of xarray ``Dataset`` and ``DataArray`` classes.
There is the :ref:`raster API <raster_api>`, such as functionality to repoject,
resample, transform, interpolate nodata or zonal statistics. There is also the
:ref:`GeoDataset API <geodataset_api>` to work with geodataset data (N-dim
point/line/polygon geometry). For example, reprojecting, transform, update geometry or
convert to geopandas.GeoDataFrame to access further GIS methods.

.. _flowpy_wrappers:

FlowPy Wrappers:
----------------

`PyFlwDir <https://deltares.github.io/pyflwdir/latest/index.html>` contains a series of
methods to work with gridded DEM and flow direction datasets. The ``gis.flw`` module
builds on top of this and provides hydrological methods for raster DEM data. For
example, calculate flow direction, flow accumulation, stream network, catchments, or
reproject hydrography.

.. _stats:

Stats:
------

The ``stats`` module has statistical methods including ``skills`` to compute skill
scores of models (e.g. NSE, KGE, bias and many more) and ``extremes`` to analyse extreme
events (extract peaks or compute return values).

.. _processes:

.. currentmodule:: hydromt.model

The ``model`` module has a ``processes`` submodule. This module contains some functions
to work with different kinds of model in- and ouput.

* :ref:`grid <workflows_grid_api>`: generic workflows to prepare regular gridded data.
    Used with the :class:`~grid.GridComponent`. For example to prepare regular grid data from constant,
    from RasterDataset (with or without reclassification) or from GeoDataFrame.
* :ref:`mesh <workflows_mesh_api>`: generic workflows to prepare unstructured mesh
    data. Used with the :class:`~mesh.MeshComponent`. For example to create a mesh grid or prepare
    unstructured mesh data from RasterDataset.
* :ref:`basin_mask <workflows_basin_api>`: workflows to prepare a basin mask based on
    different region definitions (bounding box, point coordinates, polygon etc.)
* :ref:`rivers <workflows_rivers_api>`: workflows to prepare river profile data like
    width and depth.
* :ref:`temp <workflows_forcing_api>`: workflows to prepare meteorological forcing
    data. For example to prepare precipitation, temperature, or compute
    evapotranspiration data. Advanced downscaling methods are also available within
    these workflows.

You can find a couple of detailed examples of how to use HydroMT methods and workflows in Python:

* `Working with raster data <../_examples/working_with_raster.ipynb>`_
* `Working with flow direction data <../_examples/working_with_flow_directions.ipynb>`_
* `Define hydrological model regions <../_examples/delineate_basin.ipynb>`_
* `Extreme Value Analysis <../_examples/doing_extreme_value_analysis.ipynb>`_

Models
------

You can also use HydroMT and its :class:`~model.Model` and
:class:`~components.base.ModelComponent` classes to do some analysis on your model
inputs or results. HydroMT views a model as a combination of different components to
represent the different type of inputs of a model, like ``config`` for the model run
configuration file, ``forcing`` for the dynamic forcing data of the model etc. For each
component, there are methods to ``set_<component>`` (update or add a new data layer),
``read_<component>`` and ``write_<component>``.

Here is a small example of how to use the :class:`~model.Model` class in python to plot
or analyse your model:

.. code-block:: python

    import hydromt
    # create a GridModel instance for an existing grid model saved in "grid_model" folder
    model = hydromt.GridModel(root="grid_model", mode="r")
    # read/get the grid data
    forcing = model.grid
    # plot the DEM
    grid["dem"].plot()

    # read the model results
    results = model.results
    # Get the simulated discharge
    qsim = results["qsim"]
    # Read observations using a local data catalog
    cat = hydromt.DataCatalog("data_catalog.yml")
    qobs = cat.get_geodataset("qobs")
    # Compute some skill scores like NSE
    nse = hydromt.stats.nashsutcliffe(qobs, qsim)

You can find more detailed examples on using the Model class in Python in:

* `Working with (grid) models in python <../_examples/working_with_models.ipynb>`_
* `Working with mesh models <../_examples/working_with_meshmodel.ipynb>`_

And feel free to visit some of the :ref:`plugins <plugins>` documentation to find even more examples!