wolfhece.mockup_spw_xlsx ======================== .. py:module:: wolfhece.mockup_spw_xlsx .. autoapi-nested-parse:: Author: HECE - University of Liege, Pierre Archambeau Date: 2026 Copyright (c) 2024 University of Liege. All rights reserved. This script and its content are protected by copyright law. Unauthorized copying or distribution of this file, via any medium, is strictly prohibited. Module Contents --------------- .. py:class:: LandUseType .. py:attribute:: name :type: str .. py:attribute:: runoff_coefficient :type: float .. py:attribute:: description :type: str :value: '' .. py:class:: LandUse(*args, **kwds) Bases: :py:obj:`enum.Enum` .. autoapi-inheritance-diagram:: wolfhece.mockup_spw_xlsx.LandUse :parts: 1 :private-bases: Create a collection of name/value pairs. Example enumeration: >>> class Color(Enum): ... RED = 1 ... BLUE = 2 ... GREEN = 3 Access them by: - attribute access:: >>> Color.RED - value lookup: >>> Color(1) - name lookup: >>> Color['RED'] Enumerations can be iterated over, and know how many members they have: >>> len(Color) 3 >>> list(Color) [, , ] Methods can be added to enumerations, and members can have their own attributes -- see the documentation for details. .. py:attribute:: FORESTS .. py:attribute:: GRASSLANDS .. py:attribute:: CULTIVATED_FIELDS .. py:attribute:: GREEN_ROOFS .. py:attribute:: BARE_SOIL .. py:attribute:: DRAINED_PAVING .. py:attribute:: IMPERVIOUS_SURFACES .. py:attribute:: WATER_AND_ROOFS .. py:method:: print_summary() :classmethod: Print a formatted table of all land-use types with their runoff coefficients. .. py:class:: MockupSPWXLSX Simplified stormwater retention basin sizing tool following the SPW methodology. This class computes the required retention capacity for a given site by combining IRM rainfall data (QDF / Montana curves), land-use characteristics and infiltration parameters. Main features ------------- - Management of sub-areas by land-use type (``LandUse``), each weighted by a runoff coefficient. - Chicago hyetograph computation from IRM curves for a given return period. - Retention capacity estimation accounting for infiltration (Darcy's law) and the admissible outflow to the drainage network. - Design rainfall duration and basin emptying time, computed alongside the retention capacity and exposed as read-only properties (``design_rainfall_duration``, ``emptying_time``). - Full import/export to JSON (``to_json`` / ``from_json``). Architecture and performance ---------------------------- - QDF data is cached at the class level (shared across instances); hyetographs are cached per return period at the instance level. The ``total_area`` and ``total_area_ponderated`` properties use a dirty-flag mechanism to avoid unnecessary recomputation during sensitivity analyses. - All physical quantities are handled via ``pint.Quantity``, so unit conversions are automatic and verified. Usage example ------------- >>> from wolfhece.mockup_spw_xlsx import MockupSPWXLSX, UNITS, LandUse >>> m = MockupSPWXLSX() >>> m.set_locality("Liège") >>> m.infiltration_area = 100 * UNITS.meter**2 >>> m.infiltration_darcy = 1e-5 * UNITS.meter / UNITS.second >>> m.admissible_outflow = 5.0 # L/s/ha by default >>> m.add_area("parking", LandUse.IMPERVIOUS_SURFACES, 500) >>> m.add_area("garden", LandUse.GRASSLANDS, 0.1 * UNITS.hectare) >>> vol = m.get_retention_capacity(return_period=25) # -> pint.Quantity in m³ >>> m.design_rainfall_duration.to('hour') # design storm duration >>> m.emptying_time.to('hour') # basin emptying time >>> m.to_json("my_project.json") # save >>> m2 = MockupSPWXLSX.from_json("my_project.json") # reload .. py:attribute:: _localities .. py:attribute:: _qdf_cache :type: dict[int, wolfhece.irm_qdf.Qdf_IRM] .. py:attribute:: _locality_nsi :type: int :value: 0 .. py:attribute:: _qdf_irm :value: None .. py:attribute:: areas :type: dict[str, tuple[LandUse, pint.Quantity]] .. py:attribute:: reference_area :type: pint.Quantity .. py:attribute:: _infiltration_area :type: pint.Quantity .. py:attribute:: _infiltration_darcy :type: pint.Quantity .. py:attribute:: _infiltration_security_factor :type: float :value: 2.0 .. py:attribute:: _admissible_outflow :type: pint.Quantity .. py:attribute:: _climate_delta_t :type: float :value: 0.0 .. py:attribute:: _climate_rate :type: float :value: 0.07 .. py:attribute:: _areas_dirty :type: bool :value: True .. py:attribute:: _cached_total_area :type: pint.Quantity | None :value: None .. py:attribute:: _cached_total_area_ponderated :type: pint.Quantity | None :value: None .. py:attribute:: _rainfall_cache :type: dict[int, tuple[numpy.ndarray, numpy.ndarray]] .. py:attribute:: _design_rainfall_duration :type: pint.Quantity | None :value: None .. py:attribute:: _emptying_time :type: pint.Quantity | None :value: None .. py:attribute:: _last_return_period :type: int | None :value: None .. py:attribute:: _last_retention_capacity :type: pint.Quantity | None :value: None .. py:attribute:: _last_time :type: numpy.ndarray | None :value: None .. py:attribute:: _last_rainfall :type: numpy.ndarray | None :value: None .. py:attribute:: _last_net_rainfall :type: pint.Quantity | None :value: None .. py:attribute:: _last_total_outflow :type: pint.Quantity | None :value: None .. py:attribute:: _last_stored :type: pint.Quantity | None :value: None .. py:method:: _invalidate_area_cache() Mark area-derived caches as stale. .. py:method:: add_area(key: str, land_use: LandUseType | str, area: float | pint.Quantity) Add an area with its land use type and area in square meters. :param key: A unique identifier for the area (e.g., "Area1", "Area2", etc.) :param land_use: The land use type of the area (e.g., LandUse.FORESTS.value) :param area: The area in square meters or a pint.Quantity representing the area. .. py:method:: update_area(key: str, land_use: LandUseType | str = None, area: float | pint.Quantity = None) Update an existing area with a new land use type and/or area in square meters. :param key: The unique identifier for the area to update (e.g., "Area1", "Area2", etc.) :param land_use: The new land use type of the area (e.g., LandUse.FORESTS.value). If None, the land use will not be updated. :param area: The new area in square meters or a pint.Quantity representing the area. If None, the area will not be updated. .. py:property:: total_area :type: pint.Quantity Calculate the total area by summing up all areas in the mockup. Result is cached and invalidated when areas are added or updated. .. py:method:: area_by_land_use(land_use: LandUseType) -> pint.Quantity Calculate the total area for a specific land use type. .. py:property:: total_area_ponderated :type: pint.Quantity Calculate the total area weighted by the runoff coefficients. Result is cached and invalidated when areas are added or updated. .. py:method:: set_reference_area(area: float | pint.Quantity) Set the reference area for the mockup. .. py:method:: set_locality(nsi_or_name: int | str) Set the locality NSI for the mockup. .. py:property:: locality_name :type: str Get the name of the locality based on the NSI. .. py:property:: qdf_irm Get the QDF IRM data for the locality. Uses a class-level cache keyed by NSI to avoid reloading the same data across multiple instances. .. py:method:: get_rainfall(return_period: int = 25) -> tuple[numpy.ndarray, numpy.ndarray] Get the rainfall depth for a given return period based on the QDF IRM data. Results are cached per return period for repeated calls. :param return_period: The return period in years (e.g., 10, 25, 30, 100). .. py:property:: infiltration_area :type: pint.Quantity Get the infiltration area in m^2. .. py:property:: infiltration_darcy :type: pint.Quantity Get the infiltration Darcy velocity in m/s. .. py:property:: climate_delta_t :type: float Temperature increase for climate change scenario [°C]. .. py:property:: climate_factor :type: float Multiplicative climate change factor on rainfall intensity. Computed as ``1 + climate_rate * climate_delta_t`` where *climate_rate* defaults to 0.07 (7 %/°C, Belgian convention). .. py:property:: admissible_outflow :type: pint.Quantity Get the admissible outflow in L/s/ha. .. py:method:: get_retention_capacity(return_period: int = 25) -> pint.Quantity Calculate the retention capacity based on the infiltration area and Darcy velocity. Also computes and stores the design rainfall duration and the emptying time, accessible via the ``design_rainfall_duration`` and ``emptying_time`` properties. :param return_period: The return period in years (e.g., 10, 25, 30, 100). .. py:property:: design_rainfall_duration :type: pint.Quantity Duration of the design rainfall event (filling period). Available after calling ``get_retention_capacity()``. .. py:property:: emptying_time :type: pint.Quantity Time required to fully empty the retention basin via infiltration and network outflow. Available after calling ``get_retention_capacity()``. .. py:method:: generate_report() -> str Return a multi-line text report summarising the sizing calculation. The report includes: - Locality information - Land-use area breakdown (table) - Infiltration / outflow parameters - Retention capacity, design rainfall duration, emptying time :raises RuntimeError: if ``get_retention_capacity()`` has not been called yet. .. py:method:: plot_hyetograph(ax=None, show: bool = True) Plot the Chicago hyetograph used for the last sizing. :param ax: Optional matplotlib Axes. Created if *None*. :param show: Call ``plt.show()`` at the end (ignored when *ax* is provided). :returns: The matplotlib Axes object. .. py:method:: plot_volume_balance(ax=None, show: bool = True) Plot inflow, outflow and cumulative stored volume over time. Two y-axes are used: left for flow rates [L/s], right for cumulative stored volume [m³]. :param ax: Optional matplotlib Axes. Created if *None*. :param show: Call ``plt.show()`` at the end (ignored when *ax* is provided). :returns: The matplotlib Axes object. .. py:method:: plot_land_use(ax=None, show: bool = True) Pie chart of the area distribution by land-use type. :param ax: Optional matplotlib Axes. Created if *None*. :param show: Call ``plt.show()`` at the end (ignored when *ax* is provided). :returns: The matplotlib Axes object. .. py:method:: plot_summary(show: bool = True) Generate a composite figure with the three main plots. Layout: hyetograph (top-left), volume balance (top-right), land-use pie (bottom-centre). :param show: Call ``plt.show()`` at the end. :returns: The matplotlib Figure object. .. py:attribute:: _SENSITIVITY_PARAMS :type: dict[str, tuple[str, str, str, bool]] .. py:method:: run_sensitivity(parameter: str, values, return_period: int = 25) -> pandas.DataFrame Run a sensitivity analysis by sweeping *parameter* over *values*. Supported parameters: ``"admissible_outflow"``, ``"infiltration_darcy"``, ``"infiltration_area"``, ``"infiltration_security_factor"``, ``"return_period"``. The method restores the original value of the parameter after the sweep. :param parameter: Name of the parameter to vary. :param values: Iterable of values to test. For physical quantities, give plain floats in the parameter's default unit (L/s/ha, m/s, m²) **or** ``pint.Quantity`` objects. :param return_period: Return period used for each run (ignored when *parameter* is ``"return_period"``). :returns: A ``pandas.DataFrame`` with columns ``parameter``, ``volume_m3``, ``duration_h``, ``emptying_h``. .. py:method:: plot_sensitivity(parameter: str, values, return_period: int = 25, ax=None, show: bool = True) Run a sensitivity analysis and plot *volume* vs *parameter*. See :meth:`run_sensitivity` for accepted parameters and values. :param ax: Optional matplotlib Axes. Created if *None*. :param show: Call ``plt.show()`` at the end (ignored when *ax* is provided). :returns: Tuple ``(ax, DataFrame)`` — the Axes and the results table. .. py:method:: _land_use_to_name(land_use) -> str Convert a LandUse enum or LandUseType to its enum name. .. py:method:: to_dict() -> dict Serialize the mockup to a dictionary. .. py:method:: to_json(path: str | pathlib.Path) -> None Export the mockup to a JSON file. :param path: Path to the output JSON file. .. py:method:: from_dict(data: dict) -> MockupSPWXLSX :classmethod: Create a MockupSPWXLSX instance from a dictionary. :param data: Dictionary with the mockup data. :return: A new MockupSPWXLSX instance. .. py:method:: make(locality: int | str, areas: dict[str, tuple[LandUse | str, float | pint.Quantity]], infiltration_area: float | pint.Quantity = 0, infiltration_darcy: float | pint.Quantity = 0, infiltration_security_factor: float = 2.0, admissible_outflow: float | pint.Quantity = 5.0, climate_delta_t: float = 0.0) -> MockupSPWXLSX :classmethod: Factory to create a fully configured instance in a single call. :param locality: Commune name or NIS code. :param areas: Dictionary ``{key: (land_use, area)}`` where *land_use* is a ``LandUse`` member or a string (enum name) and *area* is in m² (float) or a ``pint.Quantity``. :param infiltration_area: Infiltration surface in m² (float) or ``pint.Quantity``. :param infiltration_darcy: Hydraulic conductivity in m/s (float) or ``pint.Quantity``. :param infiltration_security_factor: Safety factor on infiltration (dimensionless, default 2). :param admissible_outflow: Allowable outflow in L/s/ha (float) or ``pint.Quantity``. :param climate_delta_t: Temperature increase for climate change scenario in °C (default 0). :return: A ready-to-use ``MockupSPWXLSX`` instance. .. rubric:: Example >>> m = MockupSPWXLSX.make( ... locality="Liège", ... areas={ ... "parking": (LandUse.IMPERVIOUS_SURFACES, 800), ... "toitures": (LandUse.WATER_AND_ROOFS, 350), ... "jardin": (LandUse.GRASSLANDS, 1500), ... }, ... infiltration_area=150, ... infiltration_darcy=1e-5, ... admissible_outflow=5.0, ... ) .. py:method:: from_json(path: str | pathlib.Path) -> MockupSPWXLSX :classmethod: Import a mockup from a JSON file. :param path: Path to the input JSON file. :return: A new MockupSPWXLSX instance.