Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 2 additions & 5 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

First of all, thanks to take time to contribute to Itzï.
This is much appreciated.
This file is a work in progress.
It will be updated as time goes.

## Code of Conduct

Expand All @@ -14,7 +12,7 @@ Please report unacceptable behaviour to [contact@itzi.org](mailto:contact@itzi.o
## How Can I Contribute?

### Reporting Bugs
If a developer don't know about a bug, he/she cannot fix it!
If a developer does not know about a bug, they cannot fix it!
Therefore, it is very valuable to thoroughly describe the problem you encounter on the bug-tracker.

### Write documentation
Expand All @@ -25,12 +23,11 @@ But writing good documentation is time consuming.
The documentation of Itzï is hosted on [Readthedocs](https://itzi.readthedocs.io).
It is written in [reStructuredText](http://docutils.sourceforge.net/rst.html)
and generated with [Sphinx](http://www.sphinx-doc.org/en/stable/).
The source code of the documentation lies on its own git [repository](https://bitbucket.org/itzi-model/itzi_docs).
The source code of the documentation is in `the docs/` directory of the main repository.

Please use one sentence per line and try to keep line length under 90 characters.
If above that threshold, break the line.


### Code

Please have a look at the [programer's manual](http://itzi.readthedocs.io/en/latest/prog_manual.html).
2 changes: 1 addition & 1 deletion src/itzi/bmi_itzi.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ def __init__(self):

# Model control functions #

def initialize(self, filename=None):
def initialize(self, filename=None) -> None:
"""Initialize the Itzï model.

Parameters
Expand Down
File renamed without changes.
2 changes: 1 addition & 1 deletion src/itzi/grass_session.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
import sys
import os
import subprocess
import importlib
import importlib.util

import itzi.messenger as msgr

Expand Down
2 changes: 1 addition & 1 deletion src/itzi/itzi.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
import itzi.itzi_error as itzi_error
import itzi.messenger as msgr
from itzi.const import VerbosityLevel
from itzi.parser import build_parser
from itzi.cli_parser import build_parser
from itzi.profiler import profile_context
from itzi.simulation_builder import SimulationBuilder
from itzi.grass_session import GrassSessionManager
Expand Down
28 changes: 14 additions & 14 deletions src/itzi/messenger.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@
GNU General Public License for more details.
"""

from typing import Callable, NoReturn
import sys
import logging
import os
from typing import NoReturn
from datetime import timedelta, datetime

from itzi.itzi_error import ItziFatal
Expand All @@ -40,7 +40,7 @@ class ItziLogger:

def __init__(self):
self.logger = logging.getLogger("itzi")
self.raise_on_error = True
self.raise_on_error: bool = True
self._setup_handlers()

def _setup_handlers(self):
Expand All @@ -66,7 +66,7 @@ def add_file_handler(self, filepath, level=logging.DEBUG):
file_handler.setFormatter(logging.Formatter("%(asctime)s - %(levelname)s - %(message)s"))
self.logger.addHandler(file_handler)

def set_verbosity(self, verbosity_level):
def set_verbosity(self, verbosity_level: int):
"""Map verbosity to logging level"""
mapping = {
VerbosityLevel.SUPER_QUIET: logging.ERROR,
Expand All @@ -83,37 +83,37 @@ def set_verbosity(self, verbosity_level):
):
handler.setLevel(level)

def fatal(self, msg) -> NoReturn:
def fatal(self, msg: str) -> NoReturn:
"""Log fatal error and raise or exit"""
self.logger.error(f"ERROR: {msg}")
if raise_on_error:
raise ItziFatal(msg)
else:
sys.exit(f"ERROR: {msg}")

def warning(self, msg) -> None:
def warning(self, msg: str) -> None:
self.logger.warning(f"WARNING: {msg}")

def message(self, msg) -> None:
def message(self, msg: str) -> None:
self.logger.info(msg)

def verbose(self, msg) -> None:
def verbose(self, msg: str) -> None:
self.logger.log(self.VERBOSE_LEVEL, msg)

def debug(self, msg) -> None:
def debug(self, msg: str) -> None:
self.logger.debug(msg)


# Global instance
_itzi_logger = ItziLogger()

# Backward-compatible module-level interface
raise_on_error = _itzi_logger.raise_on_error
fatal = _itzi_logger.fatal
warning = _itzi_logger.warning
message = _itzi_logger.message
verbose = _itzi_logger.verbose
debug = _itzi_logger.debug
raise_on_error: bool = _itzi_logger.raise_on_error
fatal: Callable[[str], NoReturn] = _itzi_logger.fatal
warning: Callable[[str], None] = _itzi_logger.warning
message: Callable[[str], None] = _itzi_logger.message
verbose: Callable[[str], None] = _itzi_logger.verbose
debug: Callable[[str], None] = _itzi_logger.debug


def percent(start_time, end_time, sim_time, sim_start_time):
Expand Down
2 changes: 1 addition & 1 deletion src/itzi/providers/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ def get_domain_data(self) -> DomainData:
@abstractmethod
def get_array(
self, map_key: str, current_time: datetime
) -> tuple[np.ndarray, datetime, datetime]:
) -> tuple[np.ndarray | None, datetime, datetime]:
"""Take a given map key and current time
return a numpy array associated with its half-open validity window `[start, end)`.

Expand Down
8 changes: 4 additions & 4 deletions src/itzi/providers/grass_input.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
class GrassRasterInputConfig(TypedDict):
grass_interface: GrassInterface
# A dict of key: input names
input_map_names: Mapping[str, str]
input_map_names: Mapping[str, str | None]
default_start_time: datetime
default_end_time: datetime

Expand All @@ -56,8 +56,8 @@ def get_domain_data(self) -> DomainData:
)

def get_map_lists(
self, map_names: Mapping[str, str]
) -> Mapping[str, list[GrassInterface.MapData]]:
self, map_names: Mapping[str, str | None]
) -> Mapping[str, list[GrassInterface.MapData] | None]:
"""Read maps names from GIS.
input map_names is a dictionary of maps/STDS names
for each entry in map_names:
Expand All @@ -69,7 +69,7 @@ def get_map_lists(
each map is stored as a MapData namedtuple
store result in instance's dictionary
"""
map_lists: dict[str, None] = {
map_lists: dict[str, list[GrassInterface.MapData] | None] = {
arr_def.key: None
for arr_def in ARRAY_DEFINITIONS
if ArrayCategory.INPUT in arr_def.category
Expand Down
9 changes: 7 additions & 2 deletions src/itzi/providers/grass_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,8 @@ def to_datetime(self, unit: str, time: int) -> datetime:
"""
return self.start_time + timedelta(seconds=self.to_s(unit, time))

def has_mask(self) -> bool:
@staticmethod
def has_mask() -> bool:
"""Return True if the mapset has a mask, False otherwise."""
return bool(gscript.read_command("g.list", type="raster", pattern="MASK"))

Expand Down Expand Up @@ -330,6 +331,10 @@ def format_id(name: str) -> str:
else:
return "@".join((name, gutils.getenv("MAPSET")))

@staticmethod
def get_current_mapset() -> str:
return gutils.getenv("MAPSET")

@staticmethod
def name_is_stds(name: str) -> bool:
"""return True if the name given as input is a registered strds
Expand All @@ -355,7 +360,7 @@ def set_null(map_id: str, threshold: float) -> None:
"""Set null values under a given threshold"""
gscript.run_command("r.null", flags="f", map=map_id, setnull=f"0.0-{threshold}")

def get_sim_extend_in_stds_unit(self, strds) -> tuple[int, int]:
def get_sim_extend_in_stds_unit(self, strds) -> tuple[int | datetime, int | datetime]:
"""Take a strds object as input
Return the simulation start_time and end_time, expressed in
the unit of the input strds
Expand Down
10 changes: 5 additions & 5 deletions src/itzi/providers/grass_output.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

from __future__ import annotations

from typing import Dict, TypedDict, Union, TYPE_CHECKING
from typing import TypedDict, TYPE_CHECKING

import numpy as np

Expand All @@ -29,7 +29,7 @@

class GrassRasterOutputConfig(TypedDict):
grass_interface: "GrassInterface"
out_map_names: Dict[str, str]
out_map_names: dict[str, str | None]
hmin: float
temporal_type: TemporalType

Expand Down Expand Up @@ -60,7 +60,7 @@ def __init__(self, config: GrassRasterOutputConfig) -> None:
self.output_maplist = {k: [] for k in self.out_map_names.keys()}

def _write_array(
self, array: np.ndarray, map_key: str, sim_time: Union[datetime, timedelta]
self, array: np.ndarray, map_key: str, sim_time: datetime | timedelta
) -> None:
"""Write simulation data for current time step."""
suffix = str(self.record_counter[map_key]).zfill(4)
Expand All @@ -75,7 +75,7 @@ def _write_array(
self.record_counter[map_key] += 1

def write_arrays(
self, array_dict: Dict[str, np.ndarray], sim_time: Union[datetime, timedelta]
self, array_dict: dict[str, np.ndarray], sim_time: datetime | timedelta
) -> None:
for arr_key, arr in array_dict.items():
if isinstance(arr, np.ndarray):
Expand Down Expand Up @@ -123,7 +123,7 @@ def __init__(self, config: GrassVectorOutputConfig) -> None:
self.vector_drainage_maplist = []

def write_vector(
self, drainage_data: DrainageNetworkData | None, sim_time: Union[datetime, timedelta]
self, drainage_data: DrainageNetworkData | None, sim_time: datetime | timedelta
) -> None:
"""Write drainage simulation data for current time step."""
if self.drainage_map_name and drainage_data:
Expand Down
20 changes: 13 additions & 7 deletions src/itzi/surfaceflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
GNU General Public License for more details.
"""

from __future__ import annotations
import math
from datetime import timedelta
from typing import TYPE_CHECKING
Expand All @@ -24,6 +25,7 @@

if TYPE_CHECKING:
from itzi.data_containers import SurfaceFlowParameters
from itzi.rasterdomain import RasterDomain


class SurfaceFlowSimulation:
Expand All @@ -36,8 +38,8 @@ class SurfaceFlowSimulation:

def __init__(
self,
domain,
flow_params: "SurfaceFlowParameters",
domain: RasterDomain,
flow_params: SurfaceFlowParameters,
):
self.dom = domain
self.dtmax = flow_params.dtmax
Expand Down Expand Up @@ -77,23 +79,27 @@ def solve_dt(self):
accommodate non-square cells
The time-step is limited by the maximum time-step dtmax.
"""
maxh = np.amax(self.dom.get_array("water_depth")) # max depth in domain
maxh = float(np.amax(self.dom.get_array("water_depth"))) # max depth in domain
min_dim = min(self.dx, self.dy)
if maxh > 0:
dt = self.cfl * (min_dim / (math.sqrt(self.g * maxh)))
self._dt = min(self.dtmax, dt)
dt = self.dt_s(self.cfl, min_dim, self.g, maxh)
self._dt = float(min(self.dtmax, dt))
else:
self._dt = self.dtmax
self._dt = float(self.dtmax)
if self._dt <= self._dt_fudge:
raise DtError(f"Tiny computed dt ({self._dt}s)")
return self

@staticmethod
def dt_s(cfl, min_dim, g, maxh):
return cfl * (min_dim / (math.sqrt(g * maxh)))

@property
def dt(self):
return timedelta(seconds=self._dt)

@dt.setter
def dt(self, newdt):
def dt(self, newdt: timedelta):
"""return an error if new dt is higher than current one or negative"""
newdt_s = newdt.total_seconds()
if self._dt is None:
Expand Down
2 changes: 1 addition & 1 deletion tests/cli/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from itzi.const import VerbosityLevel
from itzi.itzi import main, itzi_run, reconcile_hotstart_commands
from itzi.itzi_error import ItziFatal
from itzi.parser import build_parser
from itzi.cli_parser import build_parser


def test_run_parser_accepts_multiple_config_files():
Expand Down
Loading
Loading