Source code for wntr.extensions.design_verification.design

"""Apply proposed pipe-design changes to a WNTR model."""

from __future__ import annotations

import copy
import math

from wntr.network import WaterNetworkModel
from wntr.utils.check_values import _check_positive_non_zero_float

from .exceptions import InvalidDesignError
from .models import PipeDesign


[docs] def apply_pipe_design( wn: WaterNetworkModel, design: PipeDesign, ) -> tuple[WaterNetworkModel, list[dict[str, float | str]]]: """Apply selected pipe-diameter changes to a copied network. The original WaterNetworkModel is not modified. Parameters ---------- wn : WaterNetworkModel Original WNTR water-distribution network model. design : PipeDesign Proposed pipe-diameter changes in metres. Returns ------- trial_wn : WaterNetworkModel Independent copy of the original network containing the proposed diameter changes. audit_records : list of dict Records containing each pipe's original and proposed diameter. Raises ------ TypeError If wn is not a WaterNetworkModel or design is not a PipeDesign. ValueError If the design name is empty, the design contains no changes, or a proposed diameter is invalid. KeyError If a named pipe does not exist in the network. """ if not isinstance(wn, WaterNetworkModel): raise TypeError( "wn must be a WNTR WaterNetworkModel." ) if not isinstance(design, PipeDesign): raise TypeError( "design must be a PipeDesign." ) if not design.name.strip(): raise InvalidDesignError( "design name cannot be empty." ) if not design.diameters_m: raise InvalidDesignError( "design must contain at least one pipe-diameter change." ) # Work on an independent copy so the original network is protected. trial_wn = copy.deepcopy(wn) audit_records: list[dict[str, float | str]] = [] for pipe_name, proposed_diameter in design.diameters_m.items(): if pipe_name not in trial_wn.pipe_name_list: raise KeyError( f"Pipe {pipe_name!r} does not exist in the network." ) try: new_diameter_m = _check_positive_non_zero_float( proposed_diameter, f"Diameter for pipe {pipe_name!r}", ) except ValueError as error: raise InvalidDesignError(str(error)) from error if not math.isfinite(new_diameter_m): raise InvalidDesignError( f"Diameter for pipe {pipe_name!r} must be finite." ) pipe = trial_wn.get_link(pipe_name) old_diameter_m = float(pipe.diameter) pipe.diameter = new_diameter_m audit_records.append( { "pipe_name": pipe_name, "old_diameter_m": old_diameter_m, "new_diameter_m": new_diameter_m, } ) return trial_wn, audit_records