Source code for imod.mf6.vsc

from typing import Literal, Optional, Sequence, TypeAlias

import numpy as np

from imod.mf6.package import Package
from imod.mf6.utilities.dataset import assign_index
from imod.schemata import DTypeSchema

ThermalFormulationOption: TypeAlias = Literal["linear", "nonlinear"]


[docs] class Viscosity(Package): """ The Viscosity Package in MODFLOW 6 is used to account for the effects of solute concentration or temperature on fluid viscosity and thereby their effects on hydraulic conductivity and stress-package conductance. If the Viscosity package is used, the Groundwater Transport process must also be used. In addition, the flow and transport models must be part of the same simulation. The Viscosity package will adjust the conductances in the model based on the solute concentrations. Parameters ---------- reference_viscosity: float Fluid reference viscosity used in the equation of state. viscosity_concentration_slope: sequence of floats Slope of the (linear) viscosity concentration line used in the viscosity equation of state. This value will be used when ``thermal_formulation`` is equal to ``"linear"`` (the default) in the OPTIONS block. When ``thermal_formulation`` is set to ``"nonlinear"``, a value for DVISCDC must be specified though it is not used. reference_concentration: sequence of floats Reference concentration used in the viscosity equation of state. modelname: sequence of strings Name of the GroundwaterTransport (GWT) model used for the concentrations. species: sequence of str Name of the species used to calculate a viscosity value. temperature_species_name: str Name of the species to be interpreted as temperature. This species is used to calculate the temperature-dependent viscosity, using all ``thermal_`` arguments. thermal_formulation: str, optional The thermal formulation to use for the temperature-dependent viscosity. thermal_a2: float, optional Is an empirical parameter specified by the user for calculating viscosity using a nonlinear formulation. If thermal_a2 is not specified, a default value of 10.0 is assigned (Voss, 1984). thermal_a3: float, optional Is an empirical parameter specified by the user for calculating viscosity using a nonlinear formulation. If thermal_a3 is not specified, a default value of 248.37 is assigned (Voss, 1984). thermal_a4: float, optional Is an empirical parameter specified by the user for calculating viscosity using a nonlinear formulation. If thermal_a4 is not specified, a default value of 133.15 is assigned (Voss, 1984). viscosityfile: str, optional Name of the binary output file to write viscosity information. validate: bool, optional Flag to indicate whether the package should be validated upon initialization. This raises a ValidationError if package input is provided in the wrong manner. Defaults to True. Examples -------- The viscosity input for a single species called "salinity", which is simulated by a GWT model called "gwt-1" are specified as follows: >>> vsc = imod.mf6.Viscosity( ... reference_viscosity=8.904E-04, ... viscosity_concentration_slope=[1.92e-6], ... reference_concentration=[0.0], ... modelname=["gwt-1"], ... species=["salinity"], ... ) Multiple species can be specified by presenting multiple values with an associated species coordinate. Two species, called "c1" and "c2", simulated by the GWT models "gwt-1" and "gwt-2" are specified as: >>> coords = {"species": ["c1", "c2"]} >>> vsc = imod.mf6.Viscosity( ... reference_viscosity=8.904E-04, ... viscosity_concentration_slope=[1.92e-6, 3.4e-6], ... reference_concentration=[0.0, 0.0], ... modelname=["gwt-1", "gwt-2], ... species=["c1", "c2"], ... ) You can also specify thermal properties, even with a nonlinear thermal formulation. >>> coords = {"species": ["salinity", "temperature"]} >>> vsc = imod.mf6.Viscosity( ... reference_viscosity=8.904E-04, ... viscosity_concentration_slope=[1.92e-6, 0.0], ... reference_concentration=[0.0, 25.0], ... modelname=["gwt-1", "gwt-2"], ... species=["salinity", "temperature"], ... temperature_species_name="temperature", ... thermal_formulation="nonlinear", ... thermal_a2=10.0, ... thermal_a3=248.37, ... thermal_a4=133.15, ... ) """ _pkg_id = "vsc" _template = Package._initialize_template(_pkg_id) _init_schemata = { "reference_viscosity": [DTypeSchema(np.floating)], "viscosity_concentration_slope": [DTypeSchema(np.floating)], "reference_concentration": [DTypeSchema(np.floating)], } _write_schemata = {}
[docs] def __init__( self, reference_viscosity: float, viscosity_concentration_slope: Sequence[float], reference_concentration: Sequence[float], modelname: Sequence[str], species: Sequence[str], temperature_species_name: Optional[str] = None, thermal_formulation: ThermalFormulationOption = "linear", thermal_a2: float = 10.0, thermal_a3: float = 248.37, thermal_a4: float = 133.15, viscosityfile: Optional[str] = None, validate: bool = True, ): dict_dataset = { "reference_viscosity": reference_viscosity, # Assign a shared index: this also forces equal lengths "viscosity_concentration_slope": assign_index( viscosity_concentration_slope ), "reference_concentration": assign_index(reference_concentration), "modelname": assign_index(modelname), "species": assign_index(species), "temperature_species_name": temperature_species_name, "thermal_formulation": thermal_formulation, "thermal_a2": thermal_a2, "thermal_a3": thermal_a3, "thermal_a4": thermal_a4, "viscosityfile": viscosityfile, } super().__init__(dict_dataset) self._validate_init_schemata(validate)
def _render(self, directory, pkgname, globaltimes, binary): ds = self.dataset packagedata = [] for i, (a, b, c, d) in enumerate( zip( ds["viscosity_concentration_slope"].values, ds["reference_concentration"].values, ds["modelname"].values, ds["species"].values, ) ): packagedata.append((i + 1, a, b, c, d)) d = { "nviscspecies": self.dataset["species"].size, "packagedata": packagedata, } for varname in [ "temperature_species_name", "thermal_formulation", "thermal_a2", "thermal_a3", "thermal_a4", "reference_viscosity", "viscosityfile", ]: value = self.dataset[varname].values[()] if self._valid(value): d[varname] = value return self._template.render(d) def _update_transport_models(self, new_modelnames: Sequence[str]): """ The names of the transport models can change in some cases, for example when partitioning. Use this function to update the names of the transport models. """ transport_model_names = self._get_transport_model_names() if not len(transport_model_names) == len(new_modelnames): raise ValueError("the number of transport models cannot be changed.") for modelname, new_modelname in zip(transport_model_names, new_modelnames): if modelname not in new_modelname: raise ValueError( "new transport model names do not match the old ones. The new names should be equal to the old ones, with a suffix." ) self.dataset["modelname"] = assign_index(new_modelnames) def _get_transport_model_names(self) -> list[str]: """ Returns the names of the transport models used by this buoyancy package. """ return list(self.dataset["modelname"].values)