Skip to content

mibitrans.data API reference

check_input

Author: Jorrit Bakker.

Module evaluating if a dictionary contains all required (correct) parameters for analysis

DomainValueError

Bases: Exception

Exception raised for values that are outside their possible domain.

Source code in mibitrans/data/check_input.py
class DomainValueError(Exception):
    """Exception raised for values that are outside their possible domain.

    Attributes:
        message -- explanation of the error
    """

    def __init__(self, message):
        """Initialize error class."""
        self.message = message
        super().__init__(self.message)

__init__(message)

Initialize error class.

Source code in mibitrans/data/check_input.py
def __init__(self, message):
    """Initialize error class."""
    self.message = message
    super().__init__(self.message)

MissingValueError

Bases: Exception

Exception raised when one or more required parameters are missing.

Source code in mibitrans/data/check_input.py
class MissingValueError(Exception):
    """Exception raised when one or more required parameters are missing.

    Attributes:
        message -- explanation of the error
    """

    def __init__(self, message):
        """Initialize error class."""
        self.message = message
        super().__init__(self.message)

__init__(message)

Initialize error class.

Source code in mibitrans/data/check_input.py
def __init__(self, message):
    """Initialize error class."""
    self.message = message
    super().__init__(self.message)

check_dictionary(value)

Check if variable is a dictionary, and raise an error if it is not.

Source code in mibitrans/data/check_input.py
def check_dictionary(value):
    """Check if variable is a dictionary, and raise an error if it is not."""
    if not isinstance(value, dict):
        raise TypeError(f"Input must be a dict, but is {type(value)} instead.")

check_model_type(parameter, allowed_model_types)

Check if variable is of the given allowed model types, and raise an error if it is not.

Source code in mibitrans/data/check_input.py
def check_model_type(parameter, allowed_model_types):
    """Check if variable is of the given allowed model types, and raise an error if it is not."""
    if not isinstance(parameter, allowed_model_types):
        if isinstance(allowed_model_types, tuple):
            raise TypeError(
                f"Input argument model should be subclass of {allowed_model_types}, but is {type(parameter)} instead."
            )
        else:
            raise TypeError(
                f"Input argument model should be in {allowed_model_types.__subclasses__()}, "
                f"but is {type(parameter)} instead."
            )

check_time_in_domain(model, time)

Check if time input is valid, and returns the index of nearest time.

Source code in mibitrans/data/check_input.py
def check_time_in_domain(model, time):
    """Check if time input is valid, and returns the index of nearest time."""
    if time is not None:
        error = _check_numeric_positive("time", time)
        if error is not None:
            raise error
        elif time > np.max(model.t):
            warnings.warn(
                f"Desired time is larger than maximum time of model ({time} > {np.max(model.t)}). Using maximum time "
                f"of model instead."
            )
            time_pos = len(model.t) - 1
        else:
            time_pos = np.argmin(abs(model.t - time))
    else:
        time_pos = len(model.t) - 1
    return time_pos

check_x_in_domain(model, x_position)

Check if x-position input is valid, and returns the index of nearest x position.

Source code in mibitrans/data/check_input.py
def check_x_in_domain(model, x_position):
    """Check if x-position input is valid, and returns the index of nearest x position."""
    error = _check_numeric_positive("x_position", x_position)
    if error is not None:
        raise error
    if x_position > np.max(model.x):
        warnings.warn(
            f"Desired x position is outside of model domain ({x_position} > {np.max(model.x)}). "
            f"Using closest position inside model domain instead."
        )

    x_pos = np.argmin(abs(model.x - x_position))
    return x_pos

check_y_in_domain(model, y_position)

Check if y-position input is valid, and returns the index of nearest y position.

Source code in mibitrans/data/check_input.py
def check_y_in_domain(model, y_position):
    """Check if y-position input is valid, and returns the index of nearest y position."""
    error = _check_numeric("y_position", y_position)
    if error is not None:
        raise error
    if y_position > np.max(model.y):
        warnings.warn(
            f"Desired y position is outside of model domain (abs({y_position}) > {np.max(model.y)}). "
            f"Using closest position inside model domain instead."
        )

    y_pos = np.argmin(abs(model.y - y_position))
    return y_pos

validate_input_values(parameter, value)

Validate if input parameter is of correct type and in correct domain.

Source code in mibitrans/data/check_input.py
def validate_input_values(parameter, value):
    """Validate if input parameter is of correct type and in correct domain."""
    match parameter:
        # Any input for verbose argument is fine; if set to anything other than False, 0 or None, verbose is on
        case "verbose":
            error = None
        case "_on_change":
            error = None
        # Specific check for retardation, which has domain >= 1
        case "retardation":
            error = _check_numeric_retardation(parameter, value)
        # Specific check for total mass, which can be a positive float, or a specific string
        case "total_mass":
            error = _check_total_mass(parameter, value)
        # Specific check for electron acceptor utilization factor, which should be UtilizationFactor dataclass
        case "utilization_factor":
            error = _check_dataclass(parameter, value, mibitrans.data.parameter_information.UtilizationFactor)
        # Parameters which can be any float value
        case "y_position":
            error = _check_numeric(parameter, value)
        # Parameters which have domain [0,1]
        case "porosity" | "fraction_organic_carbon":
            error = _check_numeric_fraction(parameter, value)
        # Parameters which are input as single values, lists or numpy arrays
        case "source_zone_boundary" | "source_zone_concentration":
            error = _check_array_list_numeric_positive(parameter, value)
        case "electron_acceptors":
            error = _check_electron_acceptor(value)
        # All other parameters are checked as floats on positive domain
        case _:
            error = _check_numeric_positive(parameter, value)

    if error and (value is not None):
        raise error

validate_source_zones(boundary, concentration)

Validate and adapt input of source_zone_boundary and source_zone_concentration arrays.

Source code in mibitrans/data/check_input.py
def validate_source_zones(boundary, concentration):
    """Validate and adapt input of source_zone_boundary and source_zone_concentration arrays."""
    # Ensure boundary and concentration are numpy arrays
    if isinstance(boundary, (float, int, np.floating, np.integer)):
        boundary = np.array([boundary])
    else:
        boundary = np.array(boundary)

    if isinstance(concentration, (float, int, np.floating, np.integer)):
        concentration = np.array([concentration], dtype=float)
    else:
        concentration = np.array(concentration, dtype=float)

    # Each given source zone boundary should have a given concentration, and vice versa
    if boundary.shape != concentration.shape:
        raise ValueError(
            f"Length of source zone boundary ({len(boundary)}) and source zone concentration "
            f"({len(concentration)}) do not match. Make sure they are of equal length."
        )

    # Reorder source zone locations if they are not given in order from close to far from source zone center
    if len(boundary) > 1:
        if not all(boundary[:-1] <= boundary[1:]):
            sort_location = np.argsort(boundary)
            boundary.sort()
            concentration = concentration[sort_location]
            warnings.warn(
                "Source zone boundary locations should be ordered by distance from source zone center. "
                "Zone boundaries and concentrations have consequently been reordered as follows:"
                f"Source zone boundaries: {boundary}"
                f"Source zone concentrations: {concentration}"
            )
        # Superposition method only works if the zone closer to the center has higher concentration than outer zones
        if not all(concentration[:-1] > concentration[1:]):
            raise ValueError(
                "Source zone concentrations should be in descending order; no source zone can have a concentration "
                "higher than the concentration of a zone closer to source center, due to the superposition method."
            )
    return boundary, concentration

parameter_information

Author: Jorrit Bakker.

File containing various dictionaries used for evaluation of names, value types and units of input data.

ElectronAcceptors dataclass

Make object with concentrations of electron acceptors.

Dataclass which handles the entry of electron acceptor concentrations used for the instant reaction biodegradation method. As plume concentrations for reduced electron acceptor species and as difference between plume and background concentrations for the electron acceptors themselves.

delta_oxygen (float) : Difference between background oxygen and plume oxygen concentrations, in [g/m^3]. Only required for instant reaction models. delta_nitrate (float) : Difference between background nitrate and contaminant plume nitrate concentrations, in [g/m^3]. Only required for instant reaction models. ferrous_iron (float) : Ferrous iron concentration in contaminant plume, in [g/m^3]. Only required for instant reaction models. delta_sulfate (float) : Difference between background sulfate and plume sulfate concentrations, in [g/m^3]. Only required for instant reaction models. methane (float) : Methane concentration in contaminant plume, in [g/m^3]. Only required for instant reaction models.

Source code in mibitrans/data/parameter_information.py
@dataclass
class ElectronAcceptors:
    """Make object with concentrations of electron acceptors.

    Dataclass which handles the entry of electron acceptor concentrations used for the instant reaction biodegradation
    method. As plume concentrations for reduced electron acceptor species and as difference between plume and background
    concentrations for the electron acceptors themselves.

    delta_oxygen (float) : Difference between background oxygen and plume oxygen concentrations, in [g/m^3].
        Only required for instant reaction models.
    delta_nitrate (float) : Difference between background nitrate and contaminant plume nitrate concentrations,
        in [g/m^3]. Only required for instant reaction models.
    ferrous_iron (float) : Ferrous iron concentration in contaminant plume, in [g/m^3]. Only required for
        instant reaction models.
    delta_sulfate (float) : Difference between background sulfate and plume sulfate concentrations, in [g/m^3].
        Only required for instant reaction models.
    methane (float) : Methane concentration in contaminant plume, in [g/m^3]. Only required for
        instant reaction models.
    """

    delta_oxygen: float
    delta_nitrate: float
    ferrous_iron: float
    delta_sulfate: float
    methane: float

    def __setattr__(self, parameter, value):
        """Override parent method to validate input when attribute is set."""
        validate_input_values(parameter, value)
        super().__setattr__(parameter, value)

    @property
    def dictionary(self):
        """Returns electron acceptors in the form of a dictionary."""
        return dict(
            delta_oxygen=self.delta_oxygen,
            delta_nitrate=self.delta_nitrate,
            ferrous_iron=self.ferrous_iron,
            delta_sulfate=self.delta_sulfate,
            methane=self.methane,
        )

    @property
    def array(self):
        """Return electron acceptor concentrations in the form of an array, in order of [O2, NO3, Fe, SO4, CH4]."""
        return np.array([self.delta_oxygen, self.delta_nitrate, self.ferrous_iron, self.delta_sulfate, self.methane])

array property

Return electron acceptor concentrations in the form of an array, in order of [O2, NO3, Fe, SO4, CH4].

dictionary property

Returns electron acceptors in the form of a dictionary.

__setattr__(parameter, value)

Override parent method to validate input when attribute is set.

Source code in mibitrans/data/parameter_information.py
def __setattr__(self, parameter, value):
    """Override parent method to validate input when attribute is set."""
    validate_input_values(parameter, value)
    super().__setattr__(parameter, value)

UtilizationFactor dataclass

Make object containing information about electron acceptor utilization factor.

Parameters:

Name Type Description Default
util_oxygen (float)

utilization factor of oxygen, as mass of oxygen consumed per mass of biodegraded contaminant [g/g].

required
util_nitrate (float)

utilization factor of nitrate, as mass of nitrate consumed per mass of biodegraded contaminant [g/g].

required
util_ferrous_iron (float)

utilization factor of ferrous iron, as mass of ferrous iron generated per mass of biodegraded contaminant [g/g].

required
util_sulfate (float)

utilization factor of sulfate, as mass of sulfate consumed per mass of biodegraded contaminant [g/g].

required
util_methane (float)

utilization factor of methane, as mass of methane generated per mass of biodegraded contaminant [g/g].

required

Raises:

Type Description
ValueError

If input parameters are incomplete or outside the valid domain.

TypeError

If input parameters of incorrect datatype.

Source code in mibitrans/data/parameter_information.py
@dataclass
class UtilizationFactor:
    """Make object containing information about electron acceptor utilization factor.

    Args:
        util_oxygen (float) : utilization factor of oxygen, as mass of oxygen consumed
            per mass of biodegraded contaminant [g/g].
        util_nitrate (float) : utilization factor of nitrate, as mass of nitrate consumed
            per mass of biodegraded contaminant [g/g].
        util_ferrous_iron (float) : utilization factor of ferrous iron, as mass of ferrous iron generated
            per mass of biodegraded contaminant [g/g].
        util_sulfate (float) : utilization factor of sulfate, as mass of sulfate consumed
            per mass of biodegraded contaminant [g/g].
        util_methane (float) : utilization factor of methane, as mass of methane generated
            per mass of biodegraded contaminant [g/g].

    Raises:
        ValueError : If input parameters are incomplete or outside the valid domain.
        TypeError : If input parameters of incorrect datatype.

    """

    util_oxygen: float
    util_nitrate: float
    util_ferrous_iron: float
    util_sulfate: float
    util_methane: float

    def __setattr__(self, parameter, value):
        """Override parent method to validate input when attribute is set."""
        if parameter != "dictionary":
            validate_input_values(parameter, value)
        super().__setattr__(parameter, value)

    @property
    def dictionary(self):
        """Returns utilization factors in the form of a dictionary."""
        return dict(
            util_oxygen=self.util_oxygen,
            util_nitrate=self.util_nitrate,
            util_ferrous_iron=self.util_ferrous_iron,
            util_sulfate=self.util_sulfate,
            util_methane=self.util_methane,
        )

    @property
    def array(self):
        """Return utilization factors in the form of an array, in order of [O2, NO3, Fe, SO4, CH4]."""
        return np.array(
            [self.util_oxygen, self.util_nitrate, self.util_ferrous_iron, self.util_sulfate, self.util_methane]
        )

array property

Return utilization factors in the form of an array, in order of [O2, NO3, Fe, SO4, CH4].

dictionary property

Returns utilization factors in the form of a dictionary.

__setattr__(parameter, value)

Override parent method to validate input when attribute is set.

Source code in mibitrans/data/parameter_information.py
def __setattr__(self, parameter, value):
    """Override parent method to validate input when attribute is set."""
    if parameter != "dictionary":
        validate_input_values(parameter, value)
    super().__setattr__(parameter, value)

parameters

Author: Jorrit Bakker.

Module handling data input in the form of a dictionary.

AttenuationParameters dataclass

Dataclass handling parameters related to adsorption, diffusion and degradation.

Parameters:

Name Type Description Default
retardation (float)

Retardation factor for transported contaminant [-]. Default is 1.

required
decay_rate (float)

First order (linear) decay coefficient in [1/day]. Only required for linear decay models. Default is 0. Also sets corresponding half life.

required
half_life (float)

Contaminant half life for 1st order (linear) decay, in [days]. Only required for linear decay models. Default is 0. Also sets corresponding decay_rate.

required
diffusion (float)

Molecular diffusion [m2/day]. Default is 0.

required
bulk_density (float)

Soil bulk density, in [g/m^3]. Optional if retardation is specified.

required
partition_coefficient (float)

Partition coefficient of the transported contaminant to soil organic matter, in [m^3/g]. Optional if retardation is specified.

required
fraction_organic_carbon (float)

Fraction of organic material in the soil [-]. Optional if retardation is specified.

required
verbose bool

Verbose mode. Defaults to False.

False

Methods:

Name Description
calculate_retardation

Calculate retardation factor from bulk density, partition coefficient and fraction organic carbon when given porosity [-]

Raises:

Type Description
ValueError

If input parameters are incomplete or outside the valid domain.

TypeError

If input parameters of incorrect datatype.

Source code in mibitrans/data/parameters.py
@dataclass
class AttenuationParameters:
    """Dataclass handling parameters related to adsorption, diffusion and degradation.

    Args:
        retardation (float) : Retardation factor for transported contaminant [-]. Default is 1.
        decay_rate (float) : First order (linear) decay coefficient in [1/day]. Only required for linear decay models.
            Default is 0. Also sets corresponding half life.
        half_life (float) : Contaminant half life for 1st order (linear) decay, in [days]. Only required for
            linear decay models. Default is 0. Also sets corresponding decay_rate.
        diffusion (float) : Molecular diffusion [m2/day]. Default is 0.
        bulk_density (float) : Soil bulk density, in [g/m^3]. Optional if retardation is specified.
        partition_coefficient (float) : Partition coefficient of the transported contaminant to soil organic matter,
            in [m^3/g]. Optional if retardation is specified.
        fraction_organic_carbon (float) : Fraction of organic material in the soil [-].
            Optional if retardation is specified.
        verbose (bool, optional): Verbose mode. Defaults to False.

    Methods:
        calculate_retardation : Calculate retardation factor from bulk density, partition coefficient and
            fraction organic carbon when given porosity [-]

    Raises:
        ValueError : If input parameters are incomplete or outside the valid domain.
        TypeError : If input parameters of incorrect datatype.
    """

    retardation: float = 1
    decay_rate: float = 0
    half_life: float = 0
    diffusion: float = 0
    bulk_density: float = None
    partition_coefficient: float = None
    fraction_organic_carbon: float = None
    verbose: bool = False

    def __setattr__(self, parameter, value):
        """Override parent method to validate input when attribute is set."""
        validate_input_values(parameter, value)
        # Separate setattr for decay rate and half life because they should always be equivalent
        if parameter == "decay_rate" or parameter == "half_life":
            decay_rate, half_life = self._set_decay(parameter, value)
            super().__setattr__("decay_rate", decay_rate)
            super().__setattr__("half_life", half_life)
        else:
            super().__setattr__(parameter, value)

    def __post_init__(self):
        """Check argument presence, types and domain."""
        self.initialized = True

    def calculate_retardation(self, porosity: float):
        """Calculate retardation factor from soil adsorption parametrers and porosity."""
        self.retardation = (
            1 + (self.bulk_density / porosity) * self.partition_coefficient * self.fraction_organic_carbon
        )
        if self.verbose:
            print(f"Retardation factor has been calculated to be {self.retardation}.")

    def _require_linear_decay(self):
        if self.decay_rate is None and self.half_life is None:
            raise MissingValueError("Linear reaction model requires decay rate or half life.")

    def _set_decay(self, parameter, value):
        if parameter == "decay_rate" and (value != 0 or hasattr(self, "initialized")):
            decay_rate = value
            if value != 0:
                half_life = np.log(2) / value
            else:
                half_life = 0
        elif parameter == "half_life" and (value != 0 or hasattr(self, "initialized")):
            half_life = value
            if value != 0:
                decay_rate = np.log(2) / value
            else:
                decay_rate = 0
        elif parameter == "decay_rate":
            decay_rate = value
            half_life = 0
        elif parameter == "half_life":
            decay_rate = self.decay_rate
            half_life = self.half_life
        else:
            decay_rate = 0
            half_life = 0

        if self.decay_rate != decay_rate and self.decay_rate != 0 and not hasattr(self, "initialized") and value != 0:
            warnings.warn(
                "Both contaminant decay rate and half life were defined, but are not equal. "
                "Value for decay rate will be used.",
                UserWarning,
            )
            half_life = np.log(2) / self.decay_rate
            decay_rate = self.decay_rate

        return decay_rate, half_life

__post_init__()

Check argument presence, types and domain.

Source code in mibitrans/data/parameters.py
def __post_init__(self):
    """Check argument presence, types and domain."""
    self.initialized = True

__setattr__(parameter, value)

Override parent method to validate input when attribute is set.

Source code in mibitrans/data/parameters.py
def __setattr__(self, parameter, value):
    """Override parent method to validate input when attribute is set."""
    validate_input_values(parameter, value)
    # Separate setattr for decay rate and half life because they should always be equivalent
    if parameter == "decay_rate" or parameter == "half_life":
        decay_rate, half_life = self._set_decay(parameter, value)
        super().__setattr__("decay_rate", decay_rate)
        super().__setattr__("half_life", half_life)
    else:
        super().__setattr__(parameter, value)

calculate_retardation(porosity)

Calculate retardation factor from soil adsorption parametrers and porosity.

Source code in mibitrans/data/parameters.py
def calculate_retardation(self, porosity: float):
    """Calculate retardation factor from soil adsorption parametrers and porosity."""
    self.retardation = (
        1 + (self.bulk_density / porosity) * self.partition_coefficient * self.fraction_organic_carbon
    )
    if self.verbose:
        print(f"Retardation factor has been calculated to be {self.retardation}.")

HydrologicalParameters dataclass

Dataclass handling input of hydrological parameters.

Parameters:

Name Type Description Default
velocity (float)

Flow velocity in the direction of the groundwater gradient, in [m/d]. Optional if h_gradient and h_conductivity are specified.

required
h_gradient (float)

Hydraulic gradient of the groundwater, in [m/m]. Optional if velocity is specified.

required
h_conductivity (float)

Hydraulic conductivity of the aquifer, in [m/d]. Optional if velocity is specified.

required
porosity (float)

Effective soil porosity [-]

required
alpha_x (float)

The dispersivity in the x (longitudinal) direction in [m]

required
alpha_y (float)

The dispersivity in the y (transverse-horizontal) direction in [m]

required
alpha_z (float, optional)

The dispersivity in the z (transverse-vertical) direction in [m]. Defaults to 1e-10

required
verbose bool

Verbose mode. Defaults to False.

False

Raises:

Type Description
ValueError

If input parameters are incomplete or outside the valid domain.

TypeError

If input parameters of incorrect datatype.

Source code in mibitrans/data/parameters.py
@dataclass
class HydrologicalParameters:
    """Dataclass handling input of hydrological parameters.

    Args:
        velocity (float) : Flow velocity in the direction of the groundwater gradient, in [m/d]. Optional if h_gradient
            and h_conductivity are specified.
        h_gradient (float) : Hydraulic gradient of the groundwater, in [m/m]. Optional if velocity is specified.
        h_conductivity (float) : Hydraulic conductivity of the aquifer, in [m/d]. Optional if velocity is specified.
        porosity (float) : Effective soil porosity [-]
        alpha_x (float) : The dispersivity in the x (longitudinal) direction in [m]
        alpha_y (float) : The dispersivity in the y (transverse-horizontal) direction in [m]
        alpha_z (float, optional) : The dispersivity in the z (transverse-vertical) direction in [m]. Defaults to 1e-10
        verbose (bool, optional): Verbose mode. Defaults to False.

    Raises:
        ValueError : If input parameters are incomplete or outside the valid domain.
        TypeError : If input parameters of incorrect datatype.
    """

    velocity: float = None
    h_gradient: float = None
    h_conductivity: float = None
    porosity: float = None
    alpha_x: float = None
    alpha_y: float = None
    alpha_z: float = 1e-10
    verbose: bool = False

    def __setattr__(self, parameter, value):
        """Override parent method to validate input when attribute is set."""
        validate_input_values(parameter, value)
        super().__setattr__(parameter, value)

    def __post_init__(self):
        """Check argument presence, types and domain. Calculate velocity if not given."""
        self._validate_input_presence()

        # Velocity is calculated from hydraulic gradient and conductivity when both are given.
        if self.h_gradient and self.h_conductivity:
            # Giving h_gradient & h_conductivity more specific than giving velocity. Input velocity will be overridden.
            if self.velocity is not None:
                warnings.warn(
                    "Both velocity and h_gradient & h_conductivity are defined. Value for velocity will be overridden.",
                    UserWarning,
                )
            self.velocity = self.h_gradient * self.h_conductivity / self.porosity
            if self.verbose:
                print(f"Groundwater flow velocity has been calculated to be {self.velocity} m/d.")

    def _validate_input_presence(self):
        missing_arguments = []
        if self.porosity is None:
            missing_arguments.append("porosity")
        if self.alpha_x is None:
            missing_arguments.append("alpha_x")
        if self.alpha_y is None:
            missing_arguments.append("alpha_y")

        if len(missing_arguments) > 0:
            raise MissingValueError(
                f"HydrologicalParameters missing {len(missing_arguments)} arguments: {missing_arguments}."
            )

        if self.velocity is None and (self.h_gradient is None or self.h_conductivity is None):
            raise MissingValueError(
                "HydrologicalParameters missing required arguments: either velocity or both h_gradient and"
                "h_conductivity."
            )

        if self.verbose:
            print("All required hydrological input arguments are present.")

__post_init__()

Check argument presence, types and domain. Calculate velocity if not given.

Source code in mibitrans/data/parameters.py
def __post_init__(self):
    """Check argument presence, types and domain. Calculate velocity if not given."""
    self._validate_input_presence()

    # Velocity is calculated from hydraulic gradient and conductivity when both are given.
    if self.h_gradient and self.h_conductivity:
        # Giving h_gradient & h_conductivity more specific than giving velocity. Input velocity will be overridden.
        if self.velocity is not None:
            warnings.warn(
                "Both velocity and h_gradient & h_conductivity are defined. Value for velocity will be overridden.",
                UserWarning,
            )
        self.velocity = self.h_gradient * self.h_conductivity / self.porosity
        if self.verbose:
            print(f"Groundwater flow velocity has been calculated to be {self.velocity} m/d.")

__setattr__(parameter, value)

Override parent method to validate input when attribute is set.

Source code in mibitrans/data/parameters.py
def __setattr__(self, parameter, value):
    """Override parent method to validate input when attribute is set."""
    validate_input_values(parameter, value)
    super().__setattr__(parameter, value)

ModelParameters dataclass

Dataclass handling model discretization parameters.

Parameters:

Name Type Description Default
model_length (float)

Model extent in the longitudinal (x) direction in [m].

required
model_width (float)

Model extent in the transverse horizontal (y) direction in [m].

required
model_time (float)

Model duration in [days].

required
dx (float, optional)

Model grid discretization step size in the longitudinal (x) direction, in [m]. By default, dx = (model_length / 100).

required
dy (float, optional)

Model grid discretization step size in the transverse horizontal (y) direction, in [m]. By default, dy = (model_width / 50).

required
dt (float, optional)

Model time discretization step size, in [days]. By default, dt = (model_time / 10).

required
verbose bool

Verbose mode. Defaults to False.

False

Raises:

Type Description
ValueError

If input parameters are incomplete or outside the valid domain.

ValueError

If model dimensions are smaller than their given step size.

TypeError

If input parameters of incorrect datatype.

Source code in mibitrans/data/parameters.py
@dataclass
class ModelParameters:
    """Dataclass handling model discretization parameters.

    Args:
        model_length (float) : Model extent in the longitudinal (x) direction in [m].
        model_width (float) : Model extent in the transverse horizontal (y) direction in [m].
        model_time (float) : Model duration in [days].
        dx (float, optional) : Model grid discretization step size in the longitudinal (x) direction, in [m]. By
            default, dx = (model_length / 100).
        dy (float, optional) : Model grid discretization step size in the transverse horizontal (y) direction, in [m].
            By default, dy = (model_width / 50).
        dt (float, optional) : Model time discretization step size, in [days]. By default, dt = (model_time / 10).
        verbose (bool, optional): Verbose mode. Defaults to False.

    Raises:
        ValueError : If input parameters are incomplete or outside the valid domain.
        ValueError : If model dimensions are smaller than their given step size.
        TypeError : If input parameters of incorrect datatype.

    """

    model_length: float
    model_width: float
    model_time: float
    dx: float = None
    dy: float = None
    dt: float = None
    verbose: bool = False

    def __setattr__(self, parameter, value):
        """Override parent method to validate input when attribute is set."""
        validate_input_values(parameter, value)
        super().__setattr__(parameter, value)
        self._validate_stepsize(parameter)

    def _validate_stepsize(self, parameter):
        """Validate if model step size is not larger than the corresponding model dimension."""
        match parameter:
            case "dx" | "model_length":
                if self.dx is not None and self.model_length is not None:
                    if self.dx > self.model_length:
                        raise ValueError(
                            f"Model x-direction step size ({self.dx}) "
                            f"is greater than the model length ({self.model_length})."
                        )
            case "dy" | "model_width":
                if self.dy is not None and self.model_width is not None:
                    if self.dy > self.model_width:
                        raise ValueError(
                            f"Model y-direction step size ({self.dy}) "
                            f"is greater than the model width ({self.model_width})."
                        )
            case "dt" | "model_time":
                if self.dt is not None and self.model_time is not None:
                    if self.dt > self.model_time:
                        raise ValueError(
                            f"Model time step size ({self.dt}) "
                            f"is greater than the total model time ({self.model_time})."
                        )

    def __post_init__(self):
        """Set model discretization parameters if not provided."""
        if not self.dx:
            self.dx = self.model_length / 100
        if not self.dy:
            self.dy = self.model_width / 50
        if not self.dt:
            self.dt = self.model_time / 10

__post_init__()

Set model discretization parameters if not provided.

Source code in mibitrans/data/parameters.py
def __post_init__(self):
    """Set model discretization parameters if not provided."""
    if not self.dx:
        self.dx = self.model_length / 100
    if not self.dy:
        self.dy = self.model_width / 50
    if not self.dt:
        self.dt = self.model_time / 10

__setattr__(parameter, value)

Override parent method to validate input when attribute is set.

Source code in mibitrans/data/parameters.py
def __setattr__(self, parameter, value):
    """Override parent method to validate input when attribute is set."""
    validate_input_values(parameter, value)
    super().__setattr__(parameter, value)
    self._validate_stepsize(parameter)

SourceParameters dataclass

Dataclass handling source parameters. Specifying concentrations and extent of source zone.

Parameters:

Name Type Description Default
source_zone_boundary (np.ndarray)

Outer boundary of each source zone, in transverse horizontal direction (y-coordiante) [m]. y=0 is at the middle of the contaminant source. Input as numpy array of length equal to the amount of source zone. Last value in the array is the limit of the source. For a source with a single source zone, only one value is required. Source is symmetrical in the x-axis.

required
source_zone_concentration (np.ndarray)

Contaminant concentration in each source zone [g/m^3]. Input as numpy array in the same order and of the same length as specified in source_zone_boundary.

required
depth (float)

Depth (transverse vertical or z-dimension) of the source zone in [m].

required
total_mass (float | str)

Mass of contaminant present in source zone, either expressed in [g], or set to ‘infinite’. The latter meaning that the source mass and therefore, the source zone concentrations do not diminish over time.

required
verbose bool

Verbose mode. Defaults to False.

False

Raises:

Type Description
ValueError

If input parameters are incomplete or outside the valid domain.

TypeError

If input parameters of incorrect datatype.

Source code in mibitrans/data/parameters.py
@dataclass
class SourceParameters:
    """Dataclass handling source parameters. Specifying concentrations and extent of source zone.

    Args:
        source_zone_boundary (np.ndarray) : Outer boundary of each source zone, in transverse horizontal direction
            (y-coordiante) [m]. y=0 is at the middle of the contaminant source. Input as numpy array of length equal
            to the amount of source zone. Last value in the array is the limit of the source. For a source with a single
            source zone, only one value is required. Source is symmetrical in the x-axis.
        source_zone_concentration (np.ndarray) : Contaminant concentration in each source zone [g/m^3]. Input as numpy
            array in the same order and of the same length as specified in source_zone_boundary.
        depth (float) : Depth (transverse vertical or z-dimension) of the source zone in [m].
        total_mass (float | str) : Mass of contaminant present in source zone, either expressed in [g],
            or set to 'infinite'. The latter meaning that the source mass and therefore, the source zone concentrations
            do not diminish over time.
        verbose (bool, optional): Verbose mode. Defaults to False.

    Raises:
        ValueError : If input parameters are incomplete or outside the valid domain.
        TypeError : If input parameters of incorrect datatype.
    """

    source_zone_boundary: np.ndarray = None
    source_zone_concentration: np.ndarray = None
    depth: float = None
    total_mass: float | str = "infinite"
    verbose: bool = False

    def __setattr__(self, parameter, value):
        """Override parent method to validate input when attribute is set."""
        validate_input_values(parameter, value)
        if parameter == "total_mass" and (isinstance(value, str) or value == np.inf):
            value = np.inf
        super().__setattr__(parameter, value)
        # When setting source zone boundary or concentration, and both present, check validity in respect to each other.
        if parameter in ["source_zone_boundary", "source_zone_concentration"] and (
            self.source_zone_boundary is not None and self.source_zone_concentration is not None
        ):
            boundary, concentration = validate_source_zones(self.source_zone_boundary, self.source_zone_concentration)
            super().__setattr__("source_zone_boundary", boundary)
            super().__setattr__("source_zone_concentration", concentration)

    def __post_init__(self):
        """Check argument presence, types and domain."""
        self._validate_input_presence()

        # Make sure naming for infinite source mass is consistent from this point onward
        if isinstance(self.total_mass, str):
            self.total_mass = "infinite"

    def interpolate(self, n_zones, method):
        """Rediscretize source to n zones. Either through linear interpolation or using a normal distribution."""
        warnings.warn("This functionality is not implemented yet. Try again later.")
        return None

    def visualize(self):
        """Plot the source zone concentration distribution."""
        source_zone(self)

    def _validate_input_presence(self):
        # Check if all required arguments are present
        missing_arguments = []
        if self.source_zone_boundary is None:
            missing_arguments.append("source_zone_boundary")
        if self.source_zone_concentration is None:
            missing_arguments.append("source_zone_concentration")
        if self.depth is None:
            missing_arguments.append("depth")

        if len(missing_arguments) > 0:
            raise MissingValueError(
                f"SourceParameters missing {len(missing_arguments)} arguments: {missing_arguments}."
            )

__post_init__()

Check argument presence, types and domain.

Source code in mibitrans/data/parameters.py
def __post_init__(self):
    """Check argument presence, types and domain."""
    self._validate_input_presence()

    # Make sure naming for infinite source mass is consistent from this point onward
    if isinstance(self.total_mass, str):
        self.total_mass = "infinite"

__setattr__(parameter, value)

Override parent method to validate input when attribute is set.

Source code in mibitrans/data/parameters.py
def __setattr__(self, parameter, value):
    """Override parent method to validate input when attribute is set."""
    validate_input_values(parameter, value)
    if parameter == "total_mass" and (isinstance(value, str) or value == np.inf):
        value = np.inf
    super().__setattr__(parameter, value)
    # When setting source zone boundary or concentration, and both present, check validity in respect to each other.
    if parameter in ["source_zone_boundary", "source_zone_concentration"] and (
        self.source_zone_boundary is not None and self.source_zone_concentration is not None
    ):
        boundary, concentration = validate_source_zones(self.source_zone_boundary, self.source_zone_concentration)
        super().__setattr__("source_zone_boundary", boundary)
        super().__setattr__("source_zone_concentration", concentration)

interpolate(n_zones, method)

Rediscretize source to n zones. Either through linear interpolation or using a normal distribution.

Source code in mibitrans/data/parameters.py
def interpolate(self, n_zones, method):
    """Rediscretize source to n zones. Either through linear interpolation or using a normal distribution."""
    warnings.warn("This functionality is not implemented yet. Try again later.")
    return None

visualize()

Plot the source zone concentration distribution.

Source code in mibitrans/data/parameters.py
def visualize(self):
    """Plot the source zone concentration distribution."""
    source_zone(self)