Source code for hydrobricks.actions.action_glacier_evolution_area_scaling

from __future__ import annotations

from pathlib import Path

import numpy as np
import pandas as pd

from hydrobricks._exceptions import ConfigurationError
from hydrobricks._hydrobricks import (
    ActionGlacierEvolutionAreaScaling as _ActionGlacierEvolutionAreaScaling,
)
from hydrobricks.actions import Action
from hydrobricks.preprocessing.glacier_evolution_area_scaling import (
    GlacierEvolutionAreaScaling,
)


[docs] class ActionGlacierEvolutionAreaScaling(Action): """ Class for the glacier evolution based on a simple area-volume scaling. The glacier evolution is based on a simple area-volume scaling, which computes the glacier area evolution based on the glacier volume evolution for each hydro unit. The lookup table is computed by the routine preprocessing/glacier_evolution_area_scaling.py """ def __init__(self) -> None: """ Initialize ActionGlacierEvolutionAreaScaling instance. This action manages glacier evolution using area-volume scaling relationships, updating glacier areas based on volume changes for each hydro unit. """ super().__init__() self.name: str = "ActionGlacierEvolutionAreaScaling" self.action: _ActionGlacierEvolutionAreaScaling = ( _ActionGlacierEvolutionAreaScaling() )
[docs] def load_from_csv( self, dir_path: str | Path, land_cover: str = "glacier", filename_area: str = "glacier_evolution_lookup_table_area.csv", filename_volume: str = "glacier_evolution_lookup_table_volume.csv", update_month: str | int = "October", ) -> None: """ Read the glacier evolution lookup table from CSV files. The files should contain the glacier area and volume evolution (in m²) for each hydro unit and increment (typically 100). The first row should contain the hydro unit IDs for the units containing glaciers. There should be no index column in the file; the first column should contain data. Parameters ---------- dir_path Path to the directory containing the lookup table files. land_cover The land cover name to apply the changes. Default: 'glacier' filename_area Name of the lookup table file for the glacier area. Default: 'glacier_evolution_lookup_table_area.csv' filename_volume Name of the lookup table file for the glacier volume. Default: 'glacier_evolution_lookup_table_volume.csv' update_month The month to apply the changes. Full English name or number (1-12). The update will be applied at the beginning of the month, every year. Default: 'October' Raises ------ FileNotFoundError If the specified CSV files do not exist. ValueError If the CSV format is incorrect or data is inconsistent. AssertionError If hydro unit IDs or table shapes don't match between area and volume files. Examples -------- >>> action = ActionGlacierEvolutionAreaScaling() >>> action.load_from_csv('./data', land_cover='glacier', update_month='October') Example of a file (with areas in m²) ------------------------------------- 2 3 4 5 6 2500.0 48125.0 34375.0 54375.0 65000.0 2125.2 44069.6 31544.8 51044.3 61726.6 1668.2 39600.1 28428.8 47478.1 58268.9 1024.3 34555.2 24914.0 43617.1 54591.8 0 28628.9 20776.8 39371.4 50646.9 0 21024.9 15298.5 34564.7 46342.8 0 7906.0 7475.2 28921.9 41597.3 0 0 1231.4 21121.3 36034.0 0 0 0 10242.1 28498.8 """ if isinstance(dir_path, str): dir_path = Path(dir_path) full_path_area = dir_path / filename_area full_path_volume = dir_path / filename_volume lookup_table_area = pd.read_csv(full_path_area) lookup_table_volume = pd.read_csv(full_path_volume) self._populate_bounded_instance( lookup_table_area, lookup_table_volume, land_cover, update_month )
[docs] def load_from( self, obj: GlacierEvolutionAreaScaling, land_cover: str = "glacier", update_month: str | int = "October", ) -> None: """ Get the evolution lookup table from a GlacierEvolutionAreaScaling instance. Parameters ---------- obj The GlacierEvolutionAreaScaling instance containing lookup tables. land_cover The land cover name to apply the changes. Default: 'glacier' update_month The month to apply the changes. Full English name or number (1-12). The update will be applied at the beginning of the month, every year. Default: 'October' Raises ------ ConfigurationError If the object is not a GlacierEvolutionAreaScaling instance. """ if not isinstance(obj, GlacierEvolutionAreaScaling): raise ConfigurationError( "The object is not a GlacierEvolutionAreaScaling instance.", item_value=type(obj).__name__, reason="Invalid object type", ) lookup_table_area = obj.get_lookup_table_area() lookup_table_volume = obj.get_lookup_table_volume() self._populate_bounded_instance( lookup_table_area, lookup_table_volume, land_cover, update_month )
[docs] def get_month(self) -> int: """ Get the month to apply the changes. Returns ------- int The month number (1-12) when changes are applied. """ return self.action.get_month()
[docs] def get_land_cover_name(self) -> str: """ Get the land cover name (glacier name) to apply the changes. Returns ------- str The land cover name (glacier name) to apply the changes. """ return self.action.get_land_cover_name()
[docs] def get_hydro_unit_ids(self) -> np.ndarray: """ Get the lookup table hydro unit IDs. Returns ------- np.ndarray Array of hydro unit IDs (integers) for which glacier evolution is tracked. """ return self.action.get_hydro_unit_ids()
[docs] def get_lookup_table_area(self) -> np.ndarray: """ Get the lookup table areas. Returns ------- np.ndarray 2D array of glacier areas (in m²) for each hydro unit and time increment. Shape: (n_increments, n_hydro_units) """ return self.action.get_lookup_table_area()
[docs] def get_lookup_table_volume(self) -> np.ndarray: """ Get the lookup table volumes. Returns ------- np.ndarray 2D array of glacier volumes (in m³) for each hydro unit and time increment. Shape: (n_increments, n_hydro_units) """ return self.action.get_lookup_table_volume()
def _populate_bounded_instance( self, lookup_table_area: pd.DataFrame, lookup_table_volume: pd.DataFrame, land_cover: str, update_month: str | int, ) -> None: """ Populate the internal C++ instance with lookup table data. Parameters ---------- lookup_table_area DataFrame containing area evolution data with hydro unit IDs as columns. lookup_table_volume DataFrame containing volume evolution data with hydro unit IDs as columns. land_cover The land cover name to apply changes to. update_month The month (name or number) to apply changes. Raises ------ AssertionError If hydro unit IDs or table shapes don't match between area and volume. """ month_num = self._convert_month_to_number(update_month) # Get the hydro unit ids from the first row hu_ids = lookup_table_area.columns.astype(int).values hu_ids_volume = lookup_table_volume.columns.astype(int).values assert np.array_equal( hu_ids, hu_ids_volume ), "The hydro unit ids in the area and volume tables do not match." # Get the areas from the rest of the table areas = lookup_table_area.astype(float).values # Get the volumes from the rest of the table volumes = lookup_table_volume.astype(float).values assert ( areas.shape == volumes.shape ), "The areas and volumes tables do not have the same shape." self.action.add_lookup_tables(month_num, land_cover, hu_ids, areas, volumes) self.is_initialized = True