Configuration
The configuration module provides a schema-driven approach to loading and validating MOBIDIC model configurations using YAML.
Overview
Configuration files define all aspects of a MOBIDIC simulation, including:
- Basin metadata: Optional basin ID, optional parameter set ID, baricenter coordinates
- Input/output paths: Meteodata, GIS data, network, states, output directories
- Raster and vector data sources: DTM, flow direction/accumulation, soil/energy parameters, river network
- Model parameters: Organized into subsections:
soil: Hydraulic conductivity, water holding capacity, flow coefficients
energy: Thermal properties, turbulent exchange, albedo
routing: Channel routing method, wave celerity, Manning coefficient
groundwater: Model type, global loss
reservoirs: Reservoir shapefiles, stage-storage curves, regulation curves/schedules (optional)
multipliers: Multiplying factors for calibration
- Initial conditions: Initial state (hillslope runoff, soil saturation, reservoir volumes)
- Simulation settings: Time step, resampling, soil/energy balance schemes, precipitation interpolation
- Output options:
output_states: Boolean flags for state variables to save
output_states_settings: State output format (NetCDF) and intervals
output_report: Report variables to save (discharge, lateral inflow)
output_report_settings: Report format (CSV/Parquet) and reach selection
output_forcing_data: Meteorological forcing data output options
- Advanced settings: Logging level and log file
The configuration system ensures all required fields are present, validates ranges and consistency, and provides sensible defaults for optional parameters.
Functions
Load and validate MOBIDIC configuration from YAML file.
Parameters:
| Name |
Type |
Description |
Default |
config_path
|
Union[str, Path]
|
Path to the YAML configuration file.
|
required
|
Returns:
| Name | Type |
Description |
MOBIDICConfig |
MOBIDICConfig
|
Validated configuration object.
|
Raises:
| Type |
Description |
FileNotFoundError
|
If the configuration file does not exist.
|
YAMLError
|
If the YAML file is invalid.
|
ValueError
|
If the configuration does not match the schema.
|
Examples:
>>> config = load_config("examples/sample_config.yaml")
>>> print(config.basin.id)
'Basin'
Source code in mobidic/config/parser.py
| def load_config(config_path: Union[str, Path]) -> MOBIDICConfig:
"""
Load and validate MOBIDIC configuration from YAML file.
Args:
config_path: Path to the YAML configuration file.
Returns:
MOBIDICConfig: Validated configuration object.
Raises:
FileNotFoundError: If the configuration file does not exist.
yaml.YAMLError: If the YAML file is invalid.
ValueError: If the configuration does not match the schema.
Examples:
>>> config = load_config("examples/sample_config.yaml")
>>> print(config.basin.id)
'Basin'
"""
config_path = Path(config_path)
if not config_path.exists():
logger.error(f"Configuration file not found: {config_path}")
raise FileNotFoundError(f"Configuration file not found: {config_path}")
logger.info(f"Loading configuration from: {config_path}")
# Load YAML file
with open(config_path, "r", encoding="utf-8") as f:
try:
config_dict = yaml.safe_load(f)
logger.success(f"Successfully loaded YAML file: {config_path}")
except yaml.YAMLError as e:
logger.error(f"Error parsing YAML file {config_path}: {e}")
raise yaml.YAMLError(f"Error parsing YAML file {config_path}: {e}") from e
# Validate and parse with Pydantic
try:
config = MOBIDICConfig(**config_dict)
logger.info("Configuration validated successfully")
except ValidationError as e:
# Add context to the error message and re-raise
logger.error(f"Configuration validation failed for {config_path}")
raise ValueError(f"Configuration validation failed for {config_path}:\n{e}") from e
# Resolve all paths to absolute paths relative to the YAML file location
config_dir = config_path.parent.resolve()
logger.debug(f"Resolving paths relative to: {config_dir}")
def resolve_path(path_str: Union[str, Path]) -> Path:
"""Convert path to absolute, resolving relative paths from config directory."""
if not path_str:
return Path(path_str)
path = Path(path_str)
if path.is_absolute():
return path
else:
return (config_dir / path).resolve()
def is_path_field(field_info) -> bool:
"""Check if a field is a PathField type."""
annotation = field_info.annotation
# Handle Optional[PathField] - strip Optional first
origin = get_origin(annotation)
if origin is Union:
args = get_args(annotation)
# Remove NoneType if present (for Optional fields)
non_none_args = [arg for arg in args if arg is not type(None)]
# If we have a single non-None arg, check if it's Annotated
if len(non_none_args) == 1:
inner_annotation = non_none_args[0]
inner_origin = get_origin(inner_annotation)
# Check if it's Annotated[Union[str, Path], ...]
if inner_origin is Annotated:
inner_args = get_args(inner_annotation)
if len(inner_args) > 0:
base_type = inner_args[0]
base_origin = get_origin(base_type)
if base_origin is Union:
base_args = get_args(base_type)
if len(base_args) == 2 and str in base_args and Path in base_args:
return True
# Check if it's exactly Union[str, Path] (non-Optional case)
if len(non_none_args) == 2 and str in non_none_args and Path in non_none_args:
return True
# Handle non-Optional Annotated[Union[str, Path], ...]
if origin is Annotated:
args = get_args(annotation)
if len(args) > 0:
base_type = args[0]
base_origin = get_origin(base_type)
if base_origin is Union:
base_args = get_args(base_type)
if len(base_args) == 2 and str in base_args and Path in base_args:
return True
return False
def resolve_path_fields(obj):
"""Recursively resolve all PathField attributes in a Pydantic model."""
if obj is None:
return
# Get model fields metadata from the class, not the instance
obj_class = type(obj)
if hasattr(obj_class, "model_fields"):
for field_name, field_info in obj_class.model_fields.items():
field_value = getattr(obj, field_name)
# Skip None values
if field_value is None:
continue
# Check if this field is a PathField
if is_path_field(field_info):
# Resolve the path
resolved = resolve_path(field_value)
setattr(obj, field_name, resolved)
logger.debug(f"Resolved {field_name}: {field_value} -> {resolved}")
# If it's a nested model, recurse
elif hasattr(type(field_value), "model_fields"):
resolve_path_fields(field_value)
# Recursively resolve all path fields in the config
resolve_path_fields(config)
return config
|
Save MOBIDIC configuration to YAML file.
Parameters:
| Name |
Type |
Description |
Default |
config
|
MOBIDICConfig
|
Configuration object to save.
|
required
|
output_path
|
Union[str, Path]
|
Path where the YAML file will be saved.
|
required
|
Examples:
>>> save_config(config, "config/config_modified.yaml")
Source code in mobidic/config/parser.py
| def save_config(config: MOBIDICConfig, output_path: Union[str, Path]) -> None:
"""
Save MOBIDIC configuration to YAML file.
Args:
config: Configuration object to save.
output_path: Path where the YAML file will be saved.
Examples:
>>> save_config(config, "config/config_modified.yaml")
"""
output_path = Path(output_path)
logger.info(f"Saving configuration to: {output_path}")
# Convert Pydantic model to dictionary with JSON-compatible types (converts Path to str)
config_dict = config.model_dump(exclude_none=True, mode="json")
# Save to YAML file
with open(output_path, "w", encoding="utf-8") as f:
yaml.dump(
config_dict,
f,
default_flow_style=False,
sort_keys=False,
allow_unicode=True,
indent=2,
)
logger.info(f"Configuration saved successfully to: {output_path}")
|
Classes
Main configuration
Bases: BaseModel
Complete MOBIDIC configuration.
Source code in mobidic/config/schema.py
| class MOBIDICConfig(BaseModel):
"""Complete MOBIDIC configuration."""
model_config = ConfigDict(
validate_assignment=True, # Validate on assignment
)
basin: Basin
paths: Paths
vector_files: VectorFiles
raster_files: RasterFiles
raster_settings: RasterSettings
parameters: Parameters
initial_conditions: Optional[InitialConditions] = Field(default_factory=InitialConditions)
simulation: Simulation
output_states: Optional[OutputStates] = Field(default_factory=OutputStates)
output_states_settings: Optional[OutputStatesSettings] = Field(default_factory=OutputStatesSettings)
output_report: Optional[OutputReport] = Field(default_factory=OutputReport)
output_report_settings: Optional[OutputReportSettings] = Field(default_factory=OutputReportSettings)
output_forcing_data: Optional[OutputForcingData] = Field(default_factory=OutputForcingData)
hyetograph: Optional[HyetographConfig] = Field(None, description="Hyetograph generation configuration (optional)")
advanced: Optional[Advanced] = Field(default_factory=Advanced)
@model_validator(mode="after")
def check_hyetograph_config_consistency(self) -> "MOBIDICConfig":
"""Validate that hyetograph config section is provided when paths.hyetograph is specified."""
if self.paths.hyetograph is not None and self.hyetograph is None:
raise ValueError(
"The 'hyetograph' configuration section must be provided when 'paths.hyetograph' is specified."
)
return self
|
vector_files
instance-attribute
raster_files
instance-attribute
raster_settings
instance-attribute
parameters
instance-attribute
initial_conditions = Field(default_factory=InitialConditions)
class-attribute
instance-attribute
simulation
instance-attribute
output_states = Field(default_factory=OutputStates)
class-attribute
instance-attribute
output_states_settings = Field(default_factory=OutputStatesSettings)
class-attribute
instance-attribute
output_report = Field(default_factory=OutputReport)
class-attribute
instance-attribute
output_report_settings = Field(default_factory=OutputReportSettings)
class-attribute
instance-attribute
output_forcing_data = Field(default_factory=OutputForcingData)
class-attribute
instance-attribute
hyetograph = Field(None, description='Hyetograph generation configuration (optional)')
class-attribute
instance-attribute
advanced = Field(default_factory=Advanced)
class-attribute
instance-attribute
Nested models
The configuration is organized hierarchically (using nested Pydantic models):
Bases: BaseModel
Basin identification and metadata.
Optional fields
- id: Descriptive name of basin (default: empty string)
- paramset_id: Descriptive name of parameter set / scenario (default: empty string)
Source code in mobidic/config/schema.py
| class Basin(BaseModel):
"""Basin identification and metadata.
Optional fields:
- id: Descriptive name of basin (default: empty string)
- paramset_id: Descriptive name of parameter set / scenario (default: empty string)
"""
id: Optional[str] = Field("", description="Descriptive name of basin")
paramset_id: Optional[str] = Field("", description="Descriptive name of parameter set / scenario")
baricenter: BasinBaricenter
|
Bases: BaseModel
File paths for input and output data.
Source code in mobidic/config/schema.py
| class Paths(BaseModel):
"""File paths for input and output data."""
meteodata: Optional[PathField] = Field(
None, description="File where the meteo data files are stored (time-series format)"
)
meteoraster: Optional[PathField] = Field(None, description="File where the meteo data are stored (raster format)")
hyetograph: Optional[PathField] = Field(
None,
description="NetCDF file where the design hyetograph is written. "
"If provided, hyetograph generation is enabled and the 'hyetograph' configuration section must be provided.",
)
gisdata: PathField = Field(..., description="Consolidated dataset to be created by GIS preprocessing")
network: PathField = Field(..., description="Consolidated hydrographic network to be created by GIS preprocessing")
reservoirs: Optional[PathField] = Field(
None, description="Consolidated reservoirs dataset to be created by GIS preprocessing (geoparquet format)"
)
states: PathField = Field(..., description="Directory where the model states will be stored")
output: PathField = Field(..., description="Directory where output report files will be stored")
@model_validator(mode="after")
def check_meteo_paths(self) -> "Paths":
"""Validate that exactly one among meteodata, meteoraster, or hyetograph is provided."""
provided = sum(
[
self.meteodata is not None,
self.meteoraster is not None,
self.hyetograph is not None,
]
)
if provided == 0:
raise ValueError(
"Exactly one among 'meteodata', 'meteoraster', or 'hyetograph' must be provided. None were provided."
)
elif provided > 1:
raise ValueError(
"Exactly one among 'meteodata', 'meteoraster', or 'hyetograph' must be provided. "
f"Found {provided} provided."
)
return self
|
check_meteo_paths()
Validate that exactly one among meteodata, meteoraster, or hyetograph is provided.
Source code in mobidic/config/schema.py
| @model_validator(mode="after")
def check_meteo_paths(self) -> "Paths":
"""Validate that exactly one among meteodata, meteoraster, or hyetograph is provided."""
provided = sum(
[
self.meteodata is not None,
self.meteoraster is not None,
self.hyetograph is not None,
]
)
if provided == 0:
raise ValueError(
"Exactly one among 'meteodata', 'meteoraster', or 'hyetograph' must be provided. None were provided."
)
elif provided > 1:
raise ValueError(
"Exactly one among 'meteodata', 'meteoraster', or 'hyetograph' must be provided. "
f"Found {provided} provided."
)
return self
|
Bases: BaseModel
Vector file paths and settings.
Source code in mobidic/config/schema.py
| class VectorFiles(BaseModel):
"""Vector file paths and settings."""
river_network: RiverNetworkVector
|
Bases: BaseModel
Raster file paths.
Source code in mobidic/config/schema.py
| class RasterFiles(BaseModel):
"""Raster file paths."""
dtm: PathField = Field(..., description="Grid of basin elevation in meters above sea level")
flow_dir: PathField = Field(..., description="Grid of flow directions")
flow_acc: PathField = Field(..., description="Grid of flow accumulation, as number of upstream cells")
Wc0: PathField = Field(
..., description="Grid of maximum water holding capacity in soil small pores, in millimiters"
)
Wg0: PathField = Field(
..., description="Grid of maximum water holding capacity in soil large pores, in millimiters"
)
ks: PathField = Field(..., description="Grids of soil hydraulic conductivity, in millimiters per hour")
kf: Optional[PathField] = Field(
None, description="Grid of (real or ideal) aquifer conductivity, in meters per second"
)
CH: Optional[PathField] = Field(None, description="Grid of turbulent exchange coeff. for heat, non dimensional")
Alb: Optional[PathField] = Field(None, description="Grid of surface albedo, non dimensional")
Ma: Optional[PathField] = Field(
None, description="Grid of binary mask (0,1) defining the artesian aquifer extension"
)
Mf: Optional[PathField] = Field(
None, description="Grid of binary mask (0,1) defining the freatic aquifer extension"
)
gamma: Optional[PathField] = Field(None, description="Grid of percolation coefficient, in one over seconds")
kappa: Optional[PathField] = Field(None, description="Grid of adsorption coefficient, in one over seconds")
beta: Optional[PathField] = Field(None, description="Grid of hypodermic flow coefficient, in one over seconds")
alpha: Optional[PathField] = Field(None, description="Grid of hillslope flow coefficient, in one over seconds")
|
Bases: BaseModel
Raster processing settings.
Source code in mobidic/config/schema.py
| class RasterSettings(BaseModel):
"""Raster processing settings."""
flow_dir_type: Literal["Grass", "Arc"] = Field(
...,
description="Flow direction pointer type: 'Grass' for 1-8 notation or 'Arc' for 1-2-4-8-16-32-64-128 notation",
)
|
Bases: BaseModel
Global land and model parameters.
Source code in mobidic/config/schema.py
| class Parameters(BaseModel):
"""Global land and model parameters."""
soil: SoilParameters
energy: Optional[EnergyParameters] = Field(default_factory=EnergyParameters)
routing: RoutingParameters
groundwater: GroundwaterParameters
reservoirs: Optional[ReservoirParameters] = Field(default_factory=ReservoirParameters)
multipliers: Optional[Multipliers] = Field(default_factory=Multipliers)
|
Bases: BaseModel
Soil-related parameters.
Optional fields with defaults
- Wc0: Maximum water holding capacity in soil small pores (default: 0.0 mm)
- Wg0: Maximum water holding capacity in soil large pores (default: 0.0 mm)
- ks: Soil hydraulic conductivity (default: 1.0 mm/h)
- kf: Aquifer conductivity (default: 1.0e-7 m/s)
Source code in mobidic/config/schema.py
| class SoilParameters(BaseModel):
"""Soil-related parameters.
Optional fields with defaults:
- Wc0: Maximum water holding capacity in soil small pores (default: 0.0 mm)
- Wg0: Maximum water holding capacity in soil large pores (default: 0.0 mm)
- ks: Soil hydraulic conductivity (default: 1.0 mm/h)
- kf: Aquifer conductivity (default: 1.0e-7 m/s)
"""
Wc0: float = Field(
0.0, description="Default value of maximum water holding capacity in soil small pores, in millimiters"
)
Wg0: float = Field(
0.0, description="Default value of maximum water holding capacity in soil large pores, in millimiters"
)
ks: float = Field(1.0, description="Default value of soil hydraulic conductivity, in mm/h")
ks_min: Optional[float] = Field(None, description="Default value of minimum soil hydraulic conductivity, in mm/h")
ks_max: Optional[float] = Field(None, description="Default value of maximum soil hydraulic conductivity, in mm/h")
kf: float = Field(1.0e-7, description="Default value of (real or ideal) aquifer conductivity, in m/s")
gamma: float = Field(..., description="Percolation coefficient, in 1/s")
kappa: float = Field(..., description="Adsorption coefficient, in 1/s")
beta: float = Field(..., description="Hypodermic flow coefficient, in 1/s")
alpha: float = Field(..., description="Hillslope flow coefficient, in 1/s")
@field_validator("Wc0", "Wg0", "ks", "kf", "gamma", "kappa", "beta", "alpha")
@classmethod
def check_positive(cls, v: float) -> float:
"""Validate that required parameters are non-negative."""
if v < 0:
raise ValueError("Value must be non-negative")
return v
|
check_positive(v)
classmethod
Validate that required parameters are non-negative.
Source code in mobidic/config/schema.py
| @field_validator("Wc0", "Wg0", "ks", "kf", "gamma", "kappa", "beta", "alpha")
@classmethod
def check_positive(cls, v: float) -> float:
"""Validate that required parameters are non-negative."""
if v < 0:
raise ValueError("Value must be non-negative")
return v
|
Bases: BaseModel
Energy balance parameters.
This entire section is optional. If omitted from configuration, all defaults will be used.
All fields are optional with defaults
- Tconst: Deep ground temperature (default: 290.0 K)
- kaps: Soil thermal conductivity (default: 2.5 W/m/K)
- nis: Soil thermal diffusivity (default: 0.8e-6 m²/s)
- CH: Turbulent exchange coefficient for heat (default: 1e-3)
- Alb: Surface albedo (default: 0.2)
Source code in mobidic/config/schema.py
| class EnergyParameters(BaseModel):
"""Energy balance parameters.
This entire section is optional. If omitted from configuration, all defaults will be used.
All fields are optional with defaults:
- Tconst: Deep ground temperature (default: 290.0 K)
- kaps: Soil thermal conductivity (default: 2.5 W/m/K)
- nis: Soil thermal diffusivity (default: 0.8e-6 m²/s)
- CH: Turbulent exchange coefficient for heat (default: 1e-3)
- Alb: Surface albedo (default: 0.2)
"""
Tconst: float = Field(290.0, description="Deep ground temperature, in deg. Kelvin")
kaps: float = Field(2.5, description="Soil thermal conductivity, in W/m/K")
nis: float = Field(0.8e-6, description="Soil thermal diffusivity, in m²/s")
CH: float = Field(1e-3, description="Default value of turbulent exchange coeff. for heat, non dimensional")
Alb: float = Field(0.2, description="Default value of surface albedo, non dimensional")
@field_validator("Tconst", "kaps", "nis", "CH")
@classmethod
def check_positive(cls, v: float) -> float:
"""Validate that parameters are positive."""
if v <= 0:
raise ValueError("Value must be positive")
return v
@field_validator("Alb")
@classmethod
def check_albedo_range(cls, v: float) -> float:
"""Validate that albedo is in valid range."""
if not 0 <= v <= 1:
raise ValueError("Albedo must be between 0 and 1")
return v
|
check_albedo_range(v)
classmethod
Validate that albedo is in valid range.
Source code in mobidic/config/schema.py
| @field_validator("Alb")
@classmethod
def check_albedo_range(cls, v: float) -> float:
"""Validate that albedo is in valid range."""
if not 0 <= v <= 1:
raise ValueError("Albedo must be between 0 and 1")
return v
|
check_positive(v)
classmethod
Validate that parameters are positive.
Source code in mobidic/config/schema.py
| @field_validator("Tconst", "kaps", "nis", "CH")
@classmethod
def check_positive(cls, v: float) -> float:
"""Validate that parameters are positive."""
if v <= 0:
raise ValueError("Value must be positive")
return v
|
Bases: BaseModel
Channel routing parameters.
Optional fields with defaults
- Br0: Width of channels with first Strahler order (default: 1.0 m)
- NBr: Exponent of equation W = Br0*O^NBr (default: 1.5)
- n_Man: Manning roughness coefficient for channels (default: 0.03 s/m^(1/3))
Source code in mobidic/config/schema.py
| class RoutingParameters(BaseModel):
"""Channel routing parameters.
Optional fields with defaults:
- Br0: Width of channels with first Strahler order (default: 1.0 m)
- NBr: Exponent of equation W = Br0*O^NBr (default: 1.5)
- n_Man: Manning roughness coefficient for channels (default: 0.03 s/m^(1/3))
"""
method: Literal["Musk", "MuskCun", "Lag", "Linear"] = Field(..., description="Type of channel routing scheme")
wcel: float = Field(..., description="Flood wave celerity in channels, in m/s")
Br0: float = Field(1.0, description="Width of channels with first Strahler order, in meters")
NBr: float = Field(
1.5, description="Exponent of equation W = Br0*O^NBr, where W=Width of channels and O=Strahler order"
)
n_Man: float = Field(0.03, description="Manning roughness coefficient for channels, in s/m^(1/3)")
@field_validator("wcel", "Br0", "n_Man")
@classmethod
def check_positive(cls, v: float) -> float:
"""Validate that parameters are positive."""
if v <= 0:
raise ValueError("Value must be positive")
return v
@field_validator("NBr")
@classmethod
def check_nbr_range(cls, v: float) -> float:
"""Validate that NBr is greater than 1."""
if v <= 1:
raise ValueError("NBr must be greater than 1")
return v
|
check_nbr_range(v)
classmethod
Validate that NBr is greater than 1.
Source code in mobidic/config/schema.py
| @field_validator("NBr")
@classmethod
def check_nbr_range(cls, v: float) -> float:
"""Validate that NBr is greater than 1."""
if v <= 1:
raise ValueError("NBr must be greater than 1")
return v
|
check_positive(v)
classmethod
Validate that parameters are positive.
Source code in mobidic/config/schema.py
| @field_validator("wcel", "Br0", "n_Man")
@classmethod
def check_positive(cls, v: float) -> float:
"""Validate that parameters are positive."""
if v <= 0:
raise ValueError("Value must be positive")
return v
|
Bases: BaseModel
Groundwater model parameters.
Source code in mobidic/config/schema.py
| class GroundwaterParameters(BaseModel):
"""Groundwater model parameters."""
model: Literal["None", "Linear", "Linear_mult", "Dupuit", "MODFLOW"] = Field(
..., description="Groundwater model type"
)
global_loss: Optional[float] = Field(0.0, description="Global water loss from aquifers, in m³/s")
@field_validator("global_loss")
@classmethod
def check_non_negative(cls, v: Optional[float]) -> float:
"""Validate that global_loss is non-negative."""
if v is not None and v < 0:
raise ValueError("global_loss must be non-negative")
return v if v is not None else 0.0
|
check_non_negative(v)
classmethod
Validate that global_loss is non-negative.
Source code in mobidic/config/schema.py
| @field_validator("global_loss")
@classmethod
def check_non_negative(cls, v: Optional[float]) -> float:
"""Validate that global_loss is non-negative."""
if v is not None and v < 0:
raise ValueError("global_loss must be non-negative")
return v if v is not None else 0.0
|
Bases: BaseModel
Reservoir input data files.
Source code in mobidic/config/schema.py
| class ReservoirParameters(BaseModel):
"""Reservoir input data files."""
res_shape: Optional[PathField] = Field(None, description="Shapefile of reservoirs and lakes (polygon features)")
stage_storage: Optional[PathField] = Field(None, description="Reservoir stage-storage curves (CSV format)")
regulation_curves: Optional[PathField] = Field(None, description="Reservoir regulation curves (CSV format)")
regulation_schedule: Optional[PathField] = Field(None, description="Reservoir regulation schedules (CSV format)")
|
Bases: BaseModel
Parameter multipliers for calibration.
Source code in mobidic/config/schema.py
| class Multipliers(BaseModel):
"""Parameter multipliers for calibration."""
ks_factor: Optional[float] = Field(1.0, description="Multiplying factor of soil hydraulic conductivity")
Wc_factor: Optional[float] = Field(
1.0, description="Multiplying factor of maximum water holding capacity in soil small pores"
)
Wg_factor: Optional[float] = Field(
1.0, description="Multiplying factor of maximum water holding capacity in soil large pores"
)
Wg_Wc_tr: Optional[float] = Field(1.0, description="Transition factor between gravitational and capillary storage")
CH_factor: Optional[float] = Field(1.0, description="Multiplying factor of turbulent exchange coeff. for heat")
cel_factor: Optional[float] = Field(1.0, description="Multiplying factor for flood wave celerity")
chan_factor: Optional[float] = Field(0.0, description="Scale factor for fraction of channalized flow")
@field_validator("ks_factor", "Wc_factor", "Wg_factor", "Wg_Wc_tr", "CH_factor", "cel_factor")
@classmethod
def check_positive(cls, v: Optional[float]) -> float:
"""Validate that multipliers are positive."""
if v is not None and v <= 0:
raise ValueError("Multiplier must be positive")
return v if v is not None else 1.0
|
check_positive(v)
classmethod
Validate that multipliers are positive.
Source code in mobidic/config/schema.py
| @field_validator("ks_factor", "Wc_factor", "Wg_factor", "Wg_Wc_tr", "CH_factor", "cel_factor")
@classmethod
def check_positive(cls, v: Optional[float]) -> float:
"""Validate that multipliers are positive."""
if v is not None and v <= 0:
raise ValueError("Multiplier must be positive")
return v if v is not None else 1.0
|
Bases: BaseModel
Initial state conditions.
All fields are optional with defaults
- Ws: Initial depth of hillslope runoff (default: 0.0 m)
- Wcsat: Initial relative saturation of capillary soil (default: 0.3)
- Wgsat: Initial relative saturation of gravitational soil (default: 0.01)
- reservoir_volumes: Path to CSV file with initial reservoir volumes (default: None)
Source code in mobidic/config/schema.py
| class InitialConditions(BaseModel):
"""Initial state conditions.
All fields are optional with defaults:
- Ws: Initial depth of hillslope runoff (default: 0.0 m)
- Wcsat: Initial relative saturation of capillary soil (default: 0.3)
- Wgsat: Initial relative saturation of gravitational soil (default: 0.01)
- reservoir_volumes: Path to CSV file with initial reservoir volumes (default: None)
"""
Ws: Optional[float] = Field(0.0, description="Initial depth of hillslope runoff, in meters")
Wcsat: Optional[float] = Field(0.3, description="Initial relative saturation of capillary soil, non dimensional")
Wgsat: Optional[float] = Field(
0.01, description="Initial relative saturation of gravitational soil, non dimensional"
)
reservoir_volumes: Optional[PathField] = Field(
None,
description=(
"Path to CSV file with initial reservoir volumes (columns: 'reservoir_id', 'volume_m3'). "
"If not provided, initial volumes are auto-calculated as 100% capacity (volume interpolated at z_max)"
),
)
@field_validator("Ws")
@classmethod
def check_ws_non_negative(cls, v: Optional[float]) -> float:
"""Validate that Ws is non-negative."""
if v is not None and v < 0:
raise ValueError("Ws must be non-negative")
return v if v is not None else 0.0
@field_validator("Wcsat", "Wgsat")
@classmethod
def check_saturation_range(cls, v: Optional[float]) -> float:
"""Validate that saturation is in valid range [0, 1]."""
if v is not None and not 0 <= v <= 1:
raise ValueError("Saturation must be between 0 and 1")
return v
|
check_saturation_range(v)
classmethod
Validate that saturation is in valid range [0, 1].
Source code in mobidic/config/schema.py
| @field_validator("Wcsat", "Wgsat")
@classmethod
def check_saturation_range(cls, v: Optional[float]) -> float:
"""Validate that saturation is in valid range [0, 1]."""
if v is not None and not 0 <= v <= 1:
raise ValueError("Saturation must be between 0 and 1")
return v
|
check_ws_non_negative(v)
classmethod
Validate that Ws is non-negative.
Source code in mobidic/config/schema.py
| @field_validator("Ws")
@classmethod
def check_ws_non_negative(cls, v: Optional[float]) -> float:
"""Validate that Ws is non-negative."""
if v is not None and v < 0:
raise ValueError("Ws must be non-negative")
return v if v is not None else 0.0
|
Bases: BaseModel
Simulation control parameters.
Optional fields with defaults
- decimation: Decimation factor from grid data space resolution to model space resolution (default: 1)
Source code in mobidic/config/schema.py
| class Simulation(BaseModel):
"""Simulation control parameters.
Optional fields with defaults:
- decimation: Decimation factor from grid data space resolution to model space resolution (default: 1)
"""
timestep: float = Field(..., description="Data and model time step, in seconds")
decimation: int = Field(
1, description="Decimation factor from grid data space resolution to model space resolution"
)
soil_scheme: Literal["Bucket", "CN"] = Field(..., description="Type of soil hydrology scheme")
energy_balance: Literal["None", "1L", "5L", "Snow"] = Field(
..., description="Type of surface energy balance scheme"
)
precipitation_interp: Optional[Literal["Nearest", "IDW"]] = Field(
"IDW", description="Precipitation interpolation method"
)
@field_validator("timestep")
@classmethod
def check_timestep_positive(cls, v: float) -> float:
"""Validate that timestep is positive."""
if v <= 0:
raise ValueError("timestep must be positive")
return v
@field_validator("decimation")
@classmethod
def check_decimation_positive(cls, v: int) -> int:
"""Validate that decimation is a positive integer."""
if v <= 0:
raise ValueError("decimation must be a positive integer")
return v
|
check_decimation_positive(v)
classmethod
Validate that decimation is a positive integer.
Source code in mobidic/config/schema.py
| @field_validator("decimation")
@classmethod
def check_decimation_positive(cls, v: int) -> int:
"""Validate that decimation is a positive integer."""
if v <= 0:
raise ValueError("decimation must be a positive integer")
return v
|
check_timestep_positive(v)
classmethod
Validate that timestep is positive.
Source code in mobidic/config/schema.py
| @field_validator("timestep")
@classmethod
def check_timestep_positive(cls, v: float) -> float:
"""Validate that timestep is positive."""
if v <= 0:
raise ValueError("timestep must be positive")
return v
|
Bases: BaseModel
Output state options.
Source code in mobidic/config/schema.py
| class OutputStates(BaseModel):
"""Output state options."""
discharge: Optional[bool] = Field(True, description="Option to save states of river network for results analysis")
reservoir_states: Optional[bool] = Field(
False, description="Option to save states of reservoirs and lakes for results analysis"
)
soil_capillary: Optional[bool] = Field(
True, description="Option to save states of soil small pores for results analysis"
)
soil_gravitational: Optional[bool] = Field(
True, description="Option to save states of soil large pores for results analysis"
)
soil_plant: Optional[bool] = Field(
True, description="Option to save states of plant/canopy water for results analysis"
)
soil_surface: Optional[bool] = Field(
True, description="Option to save states of surface water for results analysis"
)
surface_temperature: Optional[bool] = Field(
False, description="Option to save states of land surface temperature for results analysis"
)
ground_temperature: Optional[bool] = Field(
False, description="Option to save states of ground temperature for results analysis"
)
aquifer_head: Optional[bool] = Field(False, description="Option to save states of aquifers for results analysis")
evapotranspiration: Optional[bool] = Field(
False, description="Option to save states of evapotranspiration for results analysis"
)
|
Bases: BaseModel
Output state file settings.
Source code in mobidic/config/schema.py
| class OutputStatesSettings(BaseModel):
"""Output state file settings."""
output_format: Optional[Literal["netCDF"]] = Field("netCDF", description="Format for state output files")
output_states: Optional[Literal["all", "final", "list", "None"]] = Field(
None, description="Which time steps to save: 'all', 'final', 'list', or 'None' (no states saved). Default: None"
)
output_interval: Optional[float] = Field(None, description="Time interval for state output, in seconds")
output_list: Optional[list[str]] = Field(
default_factory=list,
description="List of datetime strings (YYYY-MM-DD HH:MM:SS) to save (used when output_states='list')",
)
flushing: Optional[int] = Field(
-1,
description="How often to flush states to disk during simulation. "
"Positive integer N = flush every N timesteps, -1 = flush only at end",
)
max_file_size: Optional[float] = Field(
500.0,
description="Maximum file size for state output files, in megabytes (MB). "
"When a file reaches this size, a new chunk file will be created. Default: 500 MB",
)
@field_validator("output_interval")
@classmethod
def check_interval_positive(cls, v: Optional[float]) -> Optional[float]:
"""Validate that output_interval is positive if provided."""
if v is not None and v <= 0:
raise ValueError("output_interval must be positive")
return v
@field_validator("output_list")
@classmethod
def validate_datetime_strings(cls, v: Optional[list[str]]) -> Optional[list[str]]:
"""Validate that output_list contains valid datetime strings."""
if v is None:
return v
from datetime import datetime
for i, date_str in enumerate(v):
try:
# Try to parse as datetime
datetime.fromisoformat(date_str.replace(" ", "T"))
except (ValueError, AttributeError) as e:
raise ValueError(
f"output_list[{i}] = '{date_str}' is not a valid datetime. "
f"Expected format: 'YYYY-MM-DD HH:MM:SS'. Error: {e}"
) from e
return v
@field_validator("flushing")
@classmethod
def check_flushing_valid(cls, v: Optional[int]) -> Optional[int]:
"""Validate that flushing is either -1 or a positive integer."""
if v is None:
return -1
if v != -1 and v <= 0:
raise ValueError("flushing must be either -1 (flush at end) or a positive integer")
return v
@field_validator("max_file_size")
@classmethod
def check_max_file_size_positive(cls, v: Optional[float]) -> Optional[float]:
"""Validate that max_file_size is positive if provided."""
if v is None:
return 500.0
if v <= 0:
raise ValueError("max_file_size must be positive")
return v
@model_validator(mode="after")
def check_output_settings_consistency(self) -> "OutputStatesSettings":
"""Validate that output settings are consistent."""
if self.output_states == "list" and not self.output_list:
raise ValueError("output_list must be provided when output_states='list'")
return self
|
check_flushing_valid(v)
classmethod
Validate that flushing is either -1 or a positive integer.
Source code in mobidic/config/schema.py
| @field_validator("flushing")
@classmethod
def check_flushing_valid(cls, v: Optional[int]) -> Optional[int]:
"""Validate that flushing is either -1 or a positive integer."""
if v is None:
return -1
if v != -1 and v <= 0:
raise ValueError("flushing must be either -1 (flush at end) or a positive integer")
return v
|
check_interval_positive(v)
classmethod
Validate that output_interval is positive if provided.
Source code in mobidic/config/schema.py
| @field_validator("output_interval")
@classmethod
def check_interval_positive(cls, v: Optional[float]) -> Optional[float]:
"""Validate that output_interval is positive if provided."""
if v is not None and v <= 0:
raise ValueError("output_interval must be positive")
return v
|
check_max_file_size_positive(v)
classmethod
Validate that max_file_size is positive if provided.
Source code in mobidic/config/schema.py
| @field_validator("max_file_size")
@classmethod
def check_max_file_size_positive(cls, v: Optional[float]) -> Optional[float]:
"""Validate that max_file_size is positive if provided."""
if v is None:
return 500.0
if v <= 0:
raise ValueError("max_file_size must be positive")
return v
|
check_output_settings_consistency()
Validate that output settings are consistent.
Source code in mobidic/config/schema.py
| @model_validator(mode="after")
def check_output_settings_consistency(self) -> "OutputStatesSettings":
"""Validate that output settings are consistent."""
if self.output_states == "list" and not self.output_list:
raise ValueError("output_list must be provided when output_states='list'")
return self
|
validate_datetime_strings(v)
classmethod
Validate that output_list contains valid datetime strings.
Source code in mobidic/config/schema.py
| @field_validator("output_list")
@classmethod
def validate_datetime_strings(cls, v: Optional[list[str]]) -> Optional[list[str]]:
"""Validate that output_list contains valid datetime strings."""
if v is None:
return v
from datetime import datetime
for i, date_str in enumerate(v):
try:
# Try to parse as datetime
datetime.fromisoformat(date_str.replace(" ", "T"))
except (ValueError, AttributeError) as e:
raise ValueError(
f"output_list[{i}] = '{date_str}' is not a valid datetime. "
f"Expected format: 'YYYY-MM-DD HH:MM:SS'. Error: {e}"
) from e
return v
|
Bases: BaseModel
Output report options.
Source code in mobidic/config/schema.py
| class OutputReport(BaseModel):
"""Output report options."""
discharge: Optional[bool] = Field(True, description="Option to save discharge hydrograph at selected reach IDs")
lateral_inflow: Optional[bool] = Field(
False, description="Option to save lateral inflow hydrograph at selected reach IDs"
)
|
Bases: BaseModel
Output report file settings.
Source code in mobidic/config/schema.py
| class OutputReportSettings(BaseModel):
"""Output report file settings."""
output_format: Optional[Literal["csv", "Parquet"]] = Field("Parquet", description="Format for report output files")
report_interval: Optional[float] = Field(None, description="Time interval for report output, in seconds")
reach_selection: Optional[Literal["all", "file", "list"]] = Field(
"all", description="Method for selecting reaches to output"
)
sel_file: Optional[PathField] = Field(None, description="Path to JSON file containing reach IDs to output")
sel_list: Optional[list[int]] = Field(None, description="List of reach IDs to output")
@field_validator("report_interval")
@classmethod
def check_interval_positive(cls, v: Optional[float]) -> Optional[float]:
"""Validate that report_interval is positive if provided."""
if v is not None and v <= 0:
raise ValueError("report_interval must be positive")
return v
@model_validator(mode="after")
def check_selection_consistency(self) -> "OutputReportSettings":
"""Validate that selection method matches provided data."""
if self.reach_selection == "file" and self.sel_file is None:
raise ValueError("sel_file must be provided when reach_selection='file'")
if self.reach_selection == "list" and not self.sel_list:
raise ValueError("sel_list must be provided when reach_selection='list'")
return self
|
check_interval_positive(v)
classmethod
Validate that report_interval is positive if provided.
Source code in mobidic/config/schema.py
| @field_validator("report_interval")
@classmethod
def check_interval_positive(cls, v: Optional[float]) -> Optional[float]:
"""Validate that report_interval is positive if provided."""
if v is not None and v <= 0:
raise ValueError("report_interval must be positive")
return v
|
check_selection_consistency()
Validate that selection method matches provided data.
Source code in mobidic/config/schema.py
| @model_validator(mode="after")
def check_selection_consistency(self) -> "OutputReportSettings":
"""Validate that selection method matches provided data."""
if self.reach_selection == "file" and self.sel_file is None:
raise ValueError("sel_file must be provided when reach_selection='file'")
if self.reach_selection == "list" and not self.sel_list:
raise ValueError("sel_list must be provided when reach_selection='list'")
return self
|
Bases: BaseModel
Output forcing data options.
Source code in mobidic/config/schema.py
| class OutputForcingData(BaseModel):
"""Output forcing data options."""
meteo_data: Optional[bool] = Field(False, description="Option to save meteorological forcing data to NetCDF")
|
Bases: BaseModel
Hyetograph construction configuration.
Configuration for generating synthetic hyetographs from IDF (Intensity-Duration-Frequency)
parameters. Used to create design storm precipitation fields for simulation.
The IDF formula used is: h = ka * k * a * t^n
where:
- h is precipitation depth [mm]
- ka is the areal reduction factor (ARF) coefficient
- k is the return period factor (spatially distributed raster)
- a is the IDF scale parameter (spatially distributed raster)
- n is the IDF shape parameter (spatially distributed raster)
- t is duration [hours]
Source code in mobidic/config/schema.py
| class HyetographConfig(BaseModel):
"""Hyetograph construction configuration.
Configuration for generating synthetic hyetographs from IDF (Intensity-Duration-Frequency)
parameters. Used to create design storm precipitation fields for simulation.
The IDF formula used is: h = ka * k * a * t^n
where:
- h is precipitation depth [mm]
- ka is the areal reduction factor (ARF) coefficient
- k is the return period factor (spatially distributed raster)
- a is the IDF scale parameter (spatially distributed raster)
- n is the IDF shape parameter (spatially distributed raster)
- t is duration [hours]
"""
a_raster: PathField = Field(..., description="Path to GeoTIFF raster with IDF 'a' parameter (scale factor)")
n_raster: PathField = Field(..., description="Path to GeoTIFF raster with IDF 'n' parameter (shape/exponent)")
k_raster: PathField = Field(..., description="Path to GeoTIFF raster with IDF 'k' parameter (return period factor)")
duration_hours: int = Field(..., description="Total duration of the hyetograph in hours")
ka: float = Field(1.0, description="Areal reduction factor (ARF) coefficient")
hyetograph_type: Literal["chicago_decreasing"] = Field(
"chicago_decreasing",
description="Hyetograph construction method. Currently only 'chicago_decreasing' is implemented.",
)
timestep_hours: int = Field(1, description="Time step for hyetograph in hours")
@field_validator("duration_hours", "timestep_hours")
@classmethod
def check_positive_int(cls, v: int) -> int:
"""Validate that duration and timestep are positive."""
if v <= 0:
raise ValueError("Value must be a positive integer")
return v
@field_validator("ka")
@classmethod
def check_ka_range(cls, v: float) -> float:
"""Validate that ka is in valid range (0, 1]."""
if v <= 0 or v > 1:
raise ValueError("ka (areal reduction factor) must be in range (0, 1]")
return v
|
check_ka_range(v)
classmethod
Validate that ka is in valid range (0, 1].
Source code in mobidic/config/schema.py
| @field_validator("ka")
@classmethod
def check_ka_range(cls, v: float) -> float:
"""Validate that ka is in valid range (0, 1]."""
if v <= 0 or v > 1:
raise ValueError("ka (areal reduction factor) must be in range (0, 1]")
return v
|
check_positive_int(v)
classmethod
Validate that duration and timestep are positive.
Source code in mobidic/config/schema.py
| @field_validator("duration_hours", "timestep_hours")
@classmethod
def check_positive_int(cls, v: int) -> int:
"""Validate that duration and timestep are positive."""
if v <= 0:
raise ValueError("Value must be a positive integer")
return v
|
Bases: BaseModel
Advanced settings.
Source code in mobidic/config/schema.py
| class Advanced(BaseModel):
"""Advanced settings."""
log_level: Optional[Literal["DEBUG", "INFO", "SUCCESS", "WARNING", "ERROR"]] = Field(
"INFO", description="Logging level"
)
log_file: Optional[PathField] = Field(None, description="Path to log file")
|
Configuration structure
See the sample configuration file for a complete example with all available options and their descriptions.
Validation
The configuration system performs validation of the .yaml files with the following checks:
- Type checking: All fields are type-checked by Pydantic
- Range validation: Numeric parameters are validated against physical constraints (e.g., albedo 0-1, saturation 0-1)
- Required fields: Missing required fields raise validation errors
- Consistency checks: Inter-field dependencies are validated (e.g., if
reach_selection='file', then sel_file must be provided)
- Path validation: File paths can optionally be validated for existence
Invalid configurations will raise pydantic.ValidationError with detailed error messages.
Auxiliary files
Reach selection file
When using reach_selection='file' in the output report settings, you need to provide a JSON file containing the reach IDs to include in the output reports. The file should contain a simple JSON array of integer reach IDs (corresponding to mobidic_id values in the processed network).
Example reach_ids.json:
[
1000,
1050,
1100,
1200,
1234
]
The reach IDs correspond to the mobidic_id field in the processed river network.
Configuration example:
output_report_settings:
output_format: Parquet
reach_selection: file
sel_file: data/reach_ids.json # Path to JSON file
Reservoir CSV files
When using reservoirs (parameters.reservoirs.res_shape is set), you need to provide CSV files for stage-storage curves, regulation curves, and regulation schedules.
Stage-storage CSV (stage_storage.csv):
Defines the relationship between water stage (elevation) and reservoir volume.
reservoir_id,stage_m,volume_m3
1,219.9,0.0
1,230.0,5000000.0
1,240.0,15000000.0
1,250.0,30000000.0
1,254.9,45000000.0
Regulation curves CSV (regulation_curves.csv):
Defines stage-discharge relationships for different regulation periods (e.g., winter vs summer operations).
reservoir_id,regulation_name,stage_m,discharge_m3s
1,winter,219.9,0.0
1,winter,230.0,5.0
1,winter,240.0,20.0
1,winter,250.0,50.0
1,summer,219.9,0.0
1,summer,230.0,2.0
1,summer,240.0,10.0
1,summer,250.0,30.0
Regulation schedule CSV (regulation_schedule.csv):
Defines which regulation curve to use during different time periods.
reservoir_id,start_date,end_date,regulation_name
1,2000-01-01,2000-05-31,winter
1,2000-06-01,2000-09-30,summer
1,2000-10-01,2000-12-31,winter
1,2001-01-01,2001-05-31,winter
1,2001-06-01,2001-09-30,summer
Initial volumes CSV (initial_conditions.csv) (optional):
Defines initial reservoir volumes. If not provided, volumes are auto-calculated from z_max field in the shapefile.
reservoir_id,volume_m3
1,20000000.0
Configuration example:
parameters:
reservoirs:
res_shape: reservoirs/reservoirs.shp
stage_storage: reservoirs/stage_storage.csv
regulation_curves: reservoirs/regulation_curves.csv
regulation_schedule: reservoirs/regulation_schedule.csv
initial_conditions:
reservoir_volumes: reservoirs/initial_volumes.csv # Optional
paths:
reservoirs: output/reservoirs.parquet # Consolidated output
output_states:
reservoir_states: true # Enable reservoir state output
Logging
MOBIDICpy uses loguru for logging throughout the package. Logging is automatically configured with INFO level when the package is imported, but can be customized using either programmatic configuration or YAML configuration.
Functions
Configure the logger for MOBIDIC package.
This function configures the loguru logger with a consistent format
for use across the MOBIDIC package and example scripts.
Note
The MOBIDIC package automatically calls this function with default
settings (INFO level) when imported. Call this function again to
reconfigure logging behavior.
Parameters:
| Name |
Type |
Description |
Default |
level
|
str
|
Logging level (DEBUG, INFO, WARNING, ERROR, CRITICAL). Default: INFO.
|
'INFO'
|
format_string
|
str | None
|
Custom format string for log messages. If None, uses default format.
|
None
|
log_file
|
str | Path | None
|
Optional path to log file. If provided, logs will be written to this file
in addition to stdout.
|
None
|
colorize
|
bool
|
Whether to use colored output (default: True).
|
True
|
Examples:
>>> from mobidic.utils import configure_logger
>>> configure_logger(level="DEBUG", log_file="mobidic.log")
Source code in mobidic/utils/logging.py
| def configure_logger(
level: str = "INFO",
format_string: str | None = None,
colorize: bool = True,
log_file: str | Path | None = None,
) -> None:
"""Configure the logger for MOBIDIC package.
This function configures the loguru logger with a consistent format
for use across the MOBIDIC package and example scripts.
Note:
The MOBIDIC package automatically calls this function with default
settings (INFO level) when imported. Call this function again to
reconfigure logging behavior.
Args:
level: Logging level (DEBUG, INFO, WARNING, ERROR, CRITICAL). Default: INFO.
format_string: Custom format string for log messages. If None, uses default format.
log_file: Optional path to log file. If provided, logs will be written to this file
in addition to stdout.
colorize: Whether to use colored output (default: True).
Examples:
>>> from mobidic.utils import configure_logger
>>> configure_logger(level="DEBUG", log_file="mobidic.log")
"""
# Remove default handler
logger.remove()
# Default format string
if format_string is None:
if colorize:
if level == "DEBUG":
format_string = (
"<green>{time:YYYY-MM-DD HH:mm:ss}</green> | "
"<level>{level: <8}</level> | "
"<cyan>{name}</cyan>:<cyan>{function}</cyan>:<cyan>{line}</cyan> | "
"{message}"
)
else:
format_string = "<green>{time:YYYY-MM-DD HH:mm:ss}</green> | <level>{level: <8}</level> | {message}"
else:
format_string = "{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {message}"
# Add stdout handler
logger.add(
sys.stdout,
format=format_string,
level=level,
colorize=colorize,
)
# Add file handler if specified
if log_file is not None:
log_file = Path(log_file)
if level == "DEBUG":
format_logfile = "{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {name}:{function}:{line} | {message}"
else:
format_logfile = "{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {message}"
logger.add(
log_file,
format=format_logfile,
level=level,
rotation="10 MB", # Rotate when file reaches 10 MB
retention="30 days", # Keep logs for 30 days
compression="zip", # Compress rotated logs
)
logger.info(f"Logging to file: {log_file}")
|
Configure the logger from a MOBIDICConfig object.
This function reads the logging settings from the config’s advanced section
and configures the logger accordingly.
Parameters:
| Name |
Type |
Description |
Default |
config
|
MOBIDICConfig
|
MOBIDICConfig object containing the configuration.
|
required
|
Examples:
>>> from mobidic import load_config, configure_logger_from_config
>>> config = load_config("config.yaml")
>>> configure_logger_from_config(config)
Source code in mobidic/utils/logging.py
| def configure_logger_from_config(config: "MOBIDICConfig") -> None:
"""Configure the logger from a MOBIDICConfig object.
This function reads the logging settings from the config's advanced section
and configures the logger accordingly.
Args:
config: MOBIDICConfig object containing the configuration.
Examples:
>>> from mobidic import load_config, configure_logger_from_config
>>> config = load_config("config.yaml")
>>> configure_logger_from_config(config)
"""
log_level = config.advanced.log_level if config.advanced else "INFO"
log_file = config.advanced.log_file if config.advanced else None
configure_logger(level=log_level, log_file=log_file)
|
Programmatic configuration
Configure logging directly in your Python code:
from mobidic import configure_logger
# Set logging level
configure_logger(level="DEBUG") # or INFO, WARNING, ERROR, CRITICAL
# Configure with log file
configure_logger(level="INFO", log_file="mobidic.log")
# Disable colorization (e.g., for CI environments)
configure_logger(level="INFO", colorize=False)
YAML configuration
Configure logging via the advanced section in your configuration file:
advanced:
log_level: DEBUG # or INFO, WARNING, ERROR, CRITICAL
log_file: logs/mobidic.log # Optional
Then load configuration and apply logging settings:
from mobidic import load_config, configure_logger_from_config
# Load configuration
config = load_config("config.yaml")
# Apply logging settings from config
configure_logger_from_config(config)
# Proceed with preprocessing or simulation
gisdata = run_preprocessing(config)
Log levels
- DEBUG: Detailed information for troubleshooting (includes function names and line numbers)
- INFO: Progress updates and general information (default)
- WARNING: Non-critical issues (e.g., missing optional files, automatic fallbacks)
- ERROR: Critical failures that stop execution
- CRITICAL: Severe errors that may lead to program termination
Log output
Console (stdout):
- Always enabled with colorized output (unless colorize=False)
- Automatically adjusts format based on log level (DEBUG shows more detail)
File (optional):
- Specify via log_file parameter or config.advanced.log_file
- Automatic log rotation: rotates when file reaches 10 MB
- Log retention: keeps logs for 30 days
- Compression: rotated logs are compressed to .zip format
- Format adapts based on log level (DEBUG includes module/function/line)
Usage example
from mobidic import configure_logger, run_preprocessing, load_config
# Configure detailed logging for debugging
configure_logger(level="DEBUG", log_file="debug.log")
# Load configuration
config = load_config("basin.yaml")
# Run preprocessing (all operations will be logged)
gisdata = run_preprocessing(config)
# Output:
# 2026-01-21 10:30:45 | INFO | Starting preprocessing workflow
# 2026-01-21 10:30:45 | DEBUG | mobidic.config.parser:load_config:123 | Loading configuration from basin.yaml
# 2026-01-21 10:30:46 | INFO | Stage 1/5: Loading configuration
# 2026-01-21 10:30:46 | INFO | Stage 2/5: Reading raster data
# 2026-01-21 10:30:47 | DEBUG | mobidic.preprocessing.gis_reader:grid_to_matrix:45 | Reading raster: dtm.tif
# ...