From 51c6836928d2a29123dfa3261c72b64510c0bbd7 Mon Sep 17 00:00:00 2001 From: Eric Morway Date: Tue, 25 Oct 2022 14:53:40 -0700 Subject: [PATCH 01/31] feat(vsc): New viscosity package for GWF model type --- autotest/test_gwf_vsc01.py | 366 ++++ autotest/test_gwf_vsc02.py | 368 ++++ autotest/test_gwf_vsc03_sfr.py | 512 ++++++ autotest/test_gwf_vsc04_lak.py | 722 ++++++++ doc/MODFLOW6References.bib | 65 +- doc/ReleaseNotes/ReleaseNotes.tex | 10 +- doc/mf6io/gwf/gwf.tex | 4 + doc/mf6io/gwf/namefile.tex | 1 + doc/mf6io/gwf/vsc.tex | 73 + doc/mf6io/mf6ivar/dfn/gwf-vsc.dfn | 185 ++ .../mf6ivar/examples/gwf-vsc-example.dat | 15 + doc/mf6io/mf6ivar/md/mf6ivar.md | 29 +- doc/mf6io/mf6ivar/mf6ivar.py | 1 + doc/mf6io/mf6ivar/tex/appendixA.tex | 4 + doc/mf6io/mf6ivar/tex/gwf-vsc-desc.tex | 45 + doc/mf6io/mf6ivar/tex/gwf-vsc-dimensions.dat | 3 + doc/mf6io/mf6ivar/tex/gwf-vsc-options.dat | 6 + doc/mf6io/mf6ivar/tex/gwf-vsc-packagedata.dat | 5 + msvs/mf6core.vfproj | 2 + src/Exchange/GwfGwtExchange.f90 | 8 +- src/Model/Connection/GwfInterfaceModel.f90 | 4 +- src/Model/GroundWaterFlow/gwf3.f90 | 41 +- src/Model/GroundWaterFlow/gwf3drn8.f90 | 51 +- src/Model/GroundWaterFlow/gwf3ghb8.f90 | 20 +- src/Model/GroundWaterFlow/gwf3lak8.f90 | 55 +- src/Model/GroundWaterFlow/gwf3maw8.f90 | 77 +- src/Model/GroundWaterFlow/gwf3npf8.f90 | 78 +- src/Model/GroundWaterFlow/gwf3riv8.f90 | 19 +- src/Model/GroundWaterFlow/gwf3sfr8.f90 | 76 +- src/Model/GroundWaterFlow/gwf3vsc8.f90 | 1494 +++++++++++++++++ src/Model/ModelUtilities/BoundaryPackage.f90 | 73 +- src/Model/ModelUtilities/GwfVscInputData.f90 | 54 + 32 files changed, 4416 insertions(+), 50 deletions(-) create mode 100644 autotest/test_gwf_vsc01.py create mode 100644 autotest/test_gwf_vsc02.py create mode 100644 autotest/test_gwf_vsc03_sfr.py create mode 100644 autotest/test_gwf_vsc04_lak.py create mode 100644 doc/mf6io/gwf/vsc.tex create mode 100644 doc/mf6io/mf6ivar/dfn/gwf-vsc.dfn create mode 100644 doc/mf6io/mf6ivar/examples/gwf-vsc-example.dat create mode 100644 doc/mf6io/mf6ivar/tex/gwf-vsc-desc.tex create mode 100644 doc/mf6io/mf6ivar/tex/gwf-vsc-dimensions.dat create mode 100644 doc/mf6io/mf6ivar/tex/gwf-vsc-options.dat create mode 100644 doc/mf6io/mf6ivar/tex/gwf-vsc-packagedata.dat create mode 100644 src/Model/GroundWaterFlow/gwf3vsc8.f90 create mode 100644 src/Model/ModelUtilities/GwfVscInputData.f90 diff --git a/autotest/test_gwf_vsc01.py b/autotest/test_gwf_vsc01.py new file mode 100644 index 00000000000..70dee8eb098 --- /dev/null +++ b/autotest/test_gwf_vsc01.py @@ -0,0 +1,366 @@ +# ## Test problem for VSC +# +# Uses constant head and general-head boundaries on the left and right +# sides of the model domain, respectively, to drive flow from left to +# right. Tests that head-dependent boundary conditions are properly +# # accounting for viscosity when VSC is active. +# + +# ### VSC Problem Setup + +# Imports + +import os +import sys +import matplotlib.pyplot as plt +import flopy +import numpy as np + +# Append to system path to include the common subdirectory + +sys.path.append(os.path.join("..", "common")) + +# Import common functionality + +import config +from figspecs import USGSFigure + +mf6exe = os.path.abspath(config.mf6_exe) + +# Scenario parameters - make sure there is at least one blank line before next item + +hyd_cond = [1205.49396942506, 864.0] # Hydraulic conductivity ($m d^{-1}$) +parameters = { + "no-vsc01-bnd": {"vsc_on": False, "hydraulic_conductivity": hyd_cond[0]}, + "vsc01-bnd": {"vsc_on": True, "hydraulic_conductivity": hyd_cond[1]}, + "no-vsc01-k": {"vsc_on": False, "hydraulic_conductivity": hyd_cond[1]}, +} + +# Model units + +length_units = "cm" +time_units = "seconds" + +# Table of model parameters + +nper = 1 # Number of periods +nstp = 500 # Number of time steps +perlen = 0.5 # Simulation time length ($d$) +nlay = 1 # Number of layers +nrow = 10 # Number of rows +ncol = 80 # Number of columns +system_length = 2.0 # Length of system ($m$) +delr = 1.0 # Column width ($m$) +delc = 1.0 # Row width ($m$) +delv = 1.0 # Layer thickness +top = 1.0 # Top of the model ($m$) +initial_temperature = 35.0 # Initial temperature (unitless) +porosity = 0.26 # porosity (unitless) +K_therm = 2.0 # Thermal conductivity # ($W/m/C$) +rho_water = 1000 # Density of water ($kg/m^3$) +rho_solids = 2650 # Density of the aquifer material ($kg/m^3$) +C_p_w = 4180 # Heat Capacity of water ($J/kg/C$) +C_s = 880 # Heat capacity of the solids ($J/kg/C$) +D_m = K_therm / (porosity * rho_water * C_p_w) +rhob = (1 - porosity) * rho_solids # Bulk density ($kg/m^3$) +K_d = C_s / (rho_water * C_p_w) # Partitioning coefficient ($m^3/kg$) +inflow = 5.7024 # ($m^3/d$) + +botm = [top - k * delv for k in range(1, nlay + 1)] + +nouter, ninner = 100, 300 +hclose, rclose, relax = 1e-10, 1e-6, 0.97 + + +# ### Functions to build, write, run, and plot models +# +# MODFLOW 6 flopy GWF simulation object (sim) is returned +# + + +def build_model(key, vsc_on, hydraulic_conductivity): + print("Building model...{}".format(key)) + + # Base simulation and model name and workspace + ws = os.path.join("temp", "examples", key) + + # generate names for each model + name = "vsc01" + gwfname = "gwf-" + key + gwtname = "gwt-" + key + + sim = flopy.mf6.MFSimulation(sim_name=name, sim_ws=ws, exe_name=mf6exe) + tdis_ds = ((perlen, nstp, 1.0),) + flopy.mf6.ModflowTdis( + sim, nper=nper, perioddata=tdis_ds, time_units=time_units + ) + gwf = flopy.mf6.ModflowGwf(sim, modelname=gwfname, save_flows=True) + ims = flopy.mf6.ModflowIms( + sim, + print_option="ALL", + outer_dvclose=hclose, + outer_maximum=nouter, + under_relaxation="NONE", + inner_maximum=ninner, + inner_dvclose=hclose, + rcloserecord=rclose, + linear_acceleration="BICGSTAB", + scaling_method="NONE", + reordering_method="NONE", + relaxation_factor=relax, + filename="{}.ims".format(gwfname), + ) + sim.register_ims_package(ims, [gwfname]) + flopy.mf6.ModflowGwfdis( + gwf, + length_units=length_units, + nlay=nlay, + nrow=nrow, + ncol=ncol, + delr=delr, + delc=delc, + top=top, + botm=botm, + ) + flopy.mf6.ModflowGwfnpf( + gwf, + save_specific_discharge=True, + icelltype=0, + k=hydraulic_conductivity, + ) + flopy.mf6.ModflowGwfic(gwf, strt=0.0) + + if vsc_on: + # Instantiate viscosity (VSC) package + vsc_filerecord = "{}.vsc.bin".format(gwfname) + vsc_pd = [(0, 0.0, 20.0, gwtname, "temperature")] + flopy.mf6.ModflowGwfvsc( + gwf, + viscref=8.904e-4, + viscosity_filerecord=vsc_filerecord, + viscosityfuncrecord=[("nonlinear", 10.0, 248.37, 133.16)], + nviscspecies=len(vsc_pd), + packagedata=vsc_pd, + pname="vsc", + filename="{}.vsc".format(gwfname), + ) + + # Instantiating GHB + ghbcond = hydraulic_conductivity * delv * delc / (0.5 * delr) + ghbspd = [ + [(0, i, ncol - 1), top, ghbcond, initial_temperature] + for i in range(nrow) + ] + flopy.mf6.ModflowGwfghb( + gwf, + stress_period_data=ghbspd, + pname="GHB-1", + auxiliary="temperature", + ) + + # Instantiating CHD + chdspd = [[(0, i, 0), 2.0, initial_temperature] for i in range(nrow)] + flopy.mf6.ModflowGwfchd( + gwf, + stress_period_data=chdspd, + pname="CHD-1", + auxiliary="temperature", + ) + + # Instatiating OC + head_filerecord = "{}.hds".format(key) + budget_filerecord = "{}.bud".format(key) + flopy.mf6.ModflowGwfoc( + gwf, + head_filerecord=head_filerecord, + budget_filerecord=budget_filerecord, + saverecord=[("HEAD", "ALL"), ("BUDGET", "ALL")], + ) + + # Setup the GWT model for simulating heat transport + # ------------------------------------------------- + gwt = flopy.mf6.ModflowGwt(sim, modelname=gwtname) + imsgwt = flopy.mf6.ModflowIms( + sim, + print_option="ALL", + outer_dvclose=hclose, + outer_maximum=nouter, + under_relaxation="NONE", + inner_maximum=ninner, + inner_dvclose=hclose, + rcloserecord=rclose, + linear_acceleration="BICGSTAB", + scaling_method="NONE", + reordering_method="NONE", + relaxation_factor=relax, + filename="{}.ims".format(gwtname), + ) + sim.register_ims_package(imsgwt, [gwtname]) + flopy.mf6.ModflowGwtdis( + gwt, + length_units=length_units, + nlay=nlay, + nrow=nrow, + ncol=ncol, + delr=delr, + delc=delc, + top=top, + botm=botm, + ) + + flopy.mf6.ModflowGwtmst( + gwt, + porosity=porosity, + sorption="linear", + bulk_density=rhob, + distcoef=K_d, + pname="MST-1", + filename="{}.mst".format(gwtname), + ) + + flopy.mf6.ModflowGwtic(gwt, strt=initial_temperature) + + flopy.mf6.ModflowGwtadv(gwt, scheme="UPSTREAM") + + flopy.mf6.ModflowGwtdsp(gwt, xt3d_off=True, diffc=D_m) + + sourcerecarray = [ + ("CHD-1", "AUX", "TEMPERATURE"), + ("GHB-1", "AUX", "TEMPERATURE"), + ] + flopy.mf6.ModflowGwtssm(gwt, sources=sourcerecarray) + + flopy.mf6.ModflowGwtoc( + gwt, + concentration_filerecord="{}.ucn".format(gwtname), + saverecord=[("CONCENTRATION", "ALL")], + printrecord=[("CONCENTRATION", "LAST"), ("BUDGET", "LAST")], + ) + + flopy.mf6.ModflowGwfgwt( + sim, exgtype="GWF6-GWT6", exgmnamea=gwfname, exgmnameb=gwtname + ) + + return sim + + +# Function to write and run model files +def write_and_run_model(sim, key, silent=True): + sim.write_simulation(silent=silent) + + success, buff = sim.run_simulation(silent=False) + errmsg = f"simulation did not terminate successfully\n{buff}" + assert success, errmsg + + # digest model budgets + simpath = sim.simulation_data.mfpath.get_sim_path() + + modbud = key + ".bud" + fpth = os.path.join(simpath, modbud) + budobj = flopy.utils.CellBudgetFile(fpth, precision="double") + outbud = budobj.get_data(text=" GHB") + + return outbud[-1] + + +def confirm_run_results( + modname1, modname2, modname3, no_vsc_bud, with_vsc_bud, low_k_bud +): + + # Sum up total flow leaving the model through the GHB boundary + no_vsc_bud_last = np.array(no_vsc_bud.tolist()) + no_with_vsc_bud_last = np.array(with_vsc_bud.tolist()) + no_low_k_bud_last = np.array(low_k_bud.tolist()) + + model1_exit = no_vsc_bud_last[:, 2].sum() + model2_exit = no_with_vsc_bud_last[:, 2].sum() + model3_exit = no_low_k_bud_last[:, 2].sum() + + # Ensure models 1 & 2 give nearly identical results + assert np.allclose(model1_exit, model2_exit, atol=1e-3), ( + "VSC results not right between models: " + + modname1 + + " and " + + modname2 + ) + + # Ensure the flow leaving model 3 is less than that which leaves model 2 + assert abs(model2_exit) > abs(model3_exit), ( + "VSC results not right between models: " + + modname2 + + " and " + + modname3 + ) + + +def scenario(idx, silent=True): + # Three model runs that test model flows with and without + # viscosity active + + # Model Run 1 (Fake the effects of viscosity with pre-specified K) + # Model Run 2 (Account for the effects of viscosity) + # Model Run 3 (Ensure that base K scenario results in less flow) + # --------------------------------------------------------- + key = list(parameters.keys())[idx] + parameter_dict = parameters[key] + sim = build_model(key, **parameter_dict) + cbc_bud = write_and_run_model(sim, key, silent=silent) + + return key, cbc_bud + + +# nosetest - exclude block from this nosetest to the next nosetest +def test_01(): + # Compare model runs with and without viscosity package active + # Model 1 - no viscosity, but with elevated K's that mimic viscosity + # adjusted K values. + modname1, bnd_scen1_bud = scenario(0) + + # Model 2 - include viscosity + modname2, bnd_scen2_bud = scenario(1) + + # Model 3 - no viscosity with basecase K, flows should be reduced + modname3, bnd_scen3_bud = scenario(2) + + # Flow through model should be the same in models 1 & 2 based on + # specified K's. That is, the second model's adjusted K should + # be equal to the pre-specified K in model 1. The third model uses + # the base K of 864 m/d without simulating viscosity effects and + # should result in less flow through the model. + confirm_run_results( + modname1, + modname2, + modname3, + bnd_scen1_bud, + bnd_scen2_bud, + bnd_scen3_bud, + ) + + +# nosetest end + +if __name__ == "__main__": + # Compare model runs with and without viscosity package active + # Model 1 - no viscosity, but with elevated K's that mimic viscosity + # adjusted K values. + modname1, bnd_scen1_bud = scenario(0) + + # Model 2 - include viscosity + modname2, bnd_scen2_bud = scenario(1) + + # Model 3 - no viscosity with basecase K, flows should be reduced + modname3, bnd_scen3_bud = scenario(2) + + # Flow through model should be the same in models 1 & 2 based on + # specified K's. That is, the second model's adjusted K should + # be equal to the pre-specified K in model 1. The third model uses + # the base K of 864 m/d without simulating viscosity effects and + # should result in less flow through the model. + confirm_run_results( + modname1, + modname2, + modname3, + bnd_scen1_bud, + bnd_scen2_bud, + bnd_scen3_bud, + ) diff --git a/autotest/test_gwf_vsc02.py b/autotest/test_gwf_vsc02.py new file mode 100644 index 00000000000..0da0c106257 --- /dev/null +++ b/autotest/test_gwf_vsc02.py @@ -0,0 +1,368 @@ +# ## Test problem for VSC +# +# Uses general-head and drain boundaries on the left and right +# sides of the model domain, respectively, to drive flow from left to +# right. Tests that head-dependent boundary conditions are properly +# accounting for viscosity when VSC is active. +# + +# ### VSC Problem Setup + +# Imports + +import os +import sys +import matplotlib.pyplot as plt +import flopy +import numpy as np + +# Append to system path to include the common subdirectory + +sys.path.append(os.path.join("..", "common")) + +# Import common functionality + +import config +from figspecs import USGSFigure + +mf6exe = os.path.abspath(config.mf6_exe) + +# Setup scenario input +hyd_cond = [1205.49396942506, 864.0] # Hydraulic conductivity ($m d^{-1}$) +parameters = { + "no-vsc02-bnd": {"vsc_on": False, "hydraulic_conductivity": hyd_cond[0]}, + "vsc02-bnd": {"vsc_on": True, "hydraulic_conductivity": hyd_cond[1]}, + "no-vsc02-k": {"vsc_on": False, "hydraulic_conductivity": hyd_cond[1]}, +} + +# Model units + +length_units = "cm" +time_units = "seconds" + +# Table of model parameters + +nper = 1 # Number of periods +nstp = 500 # Number of time steps +perlen = 0.5 # Simulation time length ($d$) +nlay = 1 # Number of layers +nrow = 10 # Number of rows +ncol = 80 # Number of columns +system_length = 2.0 # Length of system ($m$) +delr = 1.0 # Column width ($m$) +delc = 1.0 # Row width ($m$) +delv = 1.0 # Layer thickness +top = 1.0 # Top of the model ($m$) +initial_temperature = 35.0 # Initial temperature (unitless) +porosity = 0.26 # porosity (unitless) +K_therm = 2.0 # Thermal conductivity # ($W/m/C$) +rho_water = 1000 # Density of water ($kg/m^3$) +rho_solids = 2650 # Density of the aquifer material ($kg/m^3$) +C_p_w = 4180 # Heat Capacity of water ($J/kg/C$) +C_s = 880 # Heat capacity of the solids ($J/kg/C$) +D_m = K_therm / (porosity * rho_water * C_p_w) +rhob = (1 - porosity) * rho_solids # Bulk density ($kg/m^3$) +K_d = C_s / (rho_water * C_p_w) # Partitioning coefficient ($m^3/kg$) +inflow = 5.7024 # ($m^3/d$) + +botm = [top - k * delv for k in range(1, nlay + 1)] + +nouter, ninner = 100, 300 +hclose, rclose, relax = 1e-10, 1e-6, 0.97 + + +# ### Functions to build, write, run, and plot models +# +# MODFLOW 6 flopy GWF simulation object (sim) is returned +# + + +def build_model(key, vsc_on, hydraulic_conductivity): + print("Building model...{}".format(key)) + + # Base simulation and model name and workspace + ws = os.path.join("temp", "examples", key) + + # generate names for each model + name = "vsc02" + gwfname = "gwf-" + key + gwtname = "gwt-" + key + + sim = flopy.mf6.MFSimulation(sim_name=name, sim_ws=ws, exe_name=mf6exe) + tdis_ds = ((perlen, nstp, 1.0),) + flopy.mf6.ModflowTdis( + sim, nper=nper, perioddata=tdis_ds, time_units=time_units + ) + gwf = flopy.mf6.ModflowGwf(sim, modelname=gwfname, save_flows=True) + ims = flopy.mf6.ModflowIms( + sim, + print_option="ALL", + outer_dvclose=hclose, + outer_maximum=nouter, + under_relaxation="NONE", + inner_maximum=ninner, + inner_dvclose=hclose, + rcloserecord=rclose, + linear_acceleration="BICGSTAB", + scaling_method="NONE", + reordering_method="NONE", + relaxation_factor=relax, + filename="{}.ims".format(gwfname), + ) + sim.register_ims_package(ims, [gwfname]) + flopy.mf6.ModflowGwfdis( + gwf, + length_units=length_units, + nlay=nlay, + nrow=nrow, + ncol=ncol, + delr=delr, + delc=delc, + top=top, + botm=botm, + ) + flopy.mf6.ModflowGwfnpf( + gwf, + save_specific_discharge=True, + icelltype=0, + k=hydraulic_conductivity, + ) + flopy.mf6.ModflowGwfic(gwf, strt=0.0) + + if vsc_on: + # Instantiate viscosity (VSC) package + vsc_filerecord = "{}.vsc.bin".format(gwfname) + vsc_pd = [(0, 0.0, 20.0, gwtname, "temperature")] + flopy.mf6.ModflowGwfvsc( + gwf, + viscref=8.904e-4, + viscosity_filerecord=vsc_filerecord, + viscosityfuncrecord=[("nonlinear", 10.0, 248.37, 133.16)], + nviscspecies=len(vsc_pd), + packagedata=vsc_pd, + pname="vsc", + filename="{}.vsc".format(gwfname), + ) + + # Instantiating GHB + ghbcond = hydraulic_conductivity * delv * delc / (0.5 * delr) + ghbspd = [ + [(0, i, 0), top + 3, ghbcond, initial_temperature] for i in range(nrow) + ] + flopy.mf6.ModflowGwfghb( + gwf, + stress_period_data=ghbspd, + pname="GHB-1", + auxiliary="temperature", + ) + + # Instantiating DRN + drnspd = [ + [(0, i, ncol - 1), top, 1.2 * ghbcond, initial_temperature] + for i in range(nrow) + ] + flopy.mf6.ModflowGwfdrn( + gwf, + stress_period_data=drnspd, + pname="DRN-1", + auxiliary="temperature", + ) + + # Instatiatingi OC + head_filerecord = "{}.hds".format(key) + budget_filerecord = "{}.bud".format(key) + flopy.mf6.ModflowGwfoc( + gwf, + head_filerecord=head_filerecord, + budget_filerecord=budget_filerecord, + saverecord=[("HEAD", "ALL"), ("BUDGET", "ALL")], + ) + + # Setup the GWT model for simulating heat transport + gwt = flopy.mf6.ModflowGwt(sim, modelname=gwtname) + imsgwt = flopy.mf6.ModflowIms( + sim, + print_option="ALL", + outer_dvclose=hclose, + outer_maximum=nouter, + under_relaxation="NONE", + inner_maximum=ninner, + inner_dvclose=hclose, + rcloserecord=rclose, + linear_acceleration="BICGSTAB", + scaling_method="NONE", + reordering_method="NONE", + relaxation_factor=relax, + filename="{}.ims".format(gwtname), + ) + sim.register_ims_package(imsgwt, [gwtname]) + flopy.mf6.ModflowGwtdis( + gwt, + length_units=length_units, + nlay=nlay, + nrow=nrow, + ncol=ncol, + delr=delr, + delc=delc, + top=top, + botm=botm, + ) + + flopy.mf6.ModflowGwtmst( + gwt, + porosity=porosity, + sorption="linear", + bulk_density=rhob, + distcoef=K_d, + pname="MST-1", + filename="{}.mst".format(gwtname), + ) + + flopy.mf6.ModflowGwtic(gwt, strt=initial_temperature) + + flopy.mf6.ModflowGwtadv(gwt, scheme="UPSTREAM") + + flopy.mf6.ModflowGwtdsp(gwt, xt3d_off=True, diffc=D_m) + + sourcerecarray = [ + ("GHB-1", "AUX", "TEMPERATURE"), + ("DRN-1", "AUX", "TEMPERATURE"), + ] + flopy.mf6.ModflowGwtssm(gwt, sources=sourcerecarray) + + flopy.mf6.ModflowGwtoc( + gwt, + concentration_filerecord="{}.ucn".format(gwtname), + saverecord=[("CONCENTRATION", "ALL")], + printrecord=[("CONCENTRATION", "LAST"), ("BUDGET", "LAST")], + ) + flopy.mf6.ModflowGwfgwt( + sim, exgtype="GWF6-GWT6", exgmnamea=gwfname, exgmnameb=gwtname + ) + return sim + + +# Function to write model files +def write_and_run_model(sim, key, silent=True): + sim.write_simulation(silent=silent) + + success, buff = sim.run_simulation(silent=False) + errmsg = f"simulation did not terminate successfully\n{buff}" + assert success, errmsg + + # digest model budgets + simpath = sim.simulation_data.mfpath.get_sim_path() + + modbud = key + ".bud" + fpth = os.path.join(simpath, modbud) + budobj = flopy.utils.CellBudgetFile(fpth, precision="double") + outbud = budobj.get_data(text=" DRN") + + return outbud[-1] + + +def confirm_run_results( + modname1, modname2, modname3, no_vsc_bud, with_vsc_bud, low_k_bud +): + + # Sum up total flow leaving the model through the GHB boundary + no_vsc_bud_last = np.array(no_vsc_bud.tolist()) + no_with_vsc_bud_last = np.array(with_vsc_bud.tolist()) + no_low_k_bud_last = np.array(low_k_bud.tolist()) + + model1_exit = no_vsc_bud_last[:, 2].sum() + model2_exit = no_with_vsc_bud_last[:, 2].sum() + model3_exit = no_low_k_bud_last[:, 2].sum() + + # Ensure models 1 & 2 give nearly identical results + assert np.allclose(model1_exit, model2_exit, atol=1e-3), ( + "VSC results not right between models: " + + modname1 + + " and " + + modname2 + ) + + # Ensure the flow leaving model 3 is less than that which leaves model 2 + assert abs(model2_exit) > abs(model3_exit), ( + "VSC results not right between models: " + + modname2 + + " and " + + modname3 + ) + + +def scenario(idx, silent=True): + # Three model runs to check relative flows w/ and w/o VSC + + # Model Run 1 - no viscosity, but uses a back-of-the-envelope + # calculated viscosity that gives same result as + # model run 2 + # Model Run 2 - includes viscosity, should give same results as + # model run 1 after applying viscosity ratio to + # prescribed K's + # Model Run 3 - no viscosity, uses same K as scenario 2 and should + # result in reduced flow relative to model run 2 + # --------------------------------------------------------- + key = list(parameters.keys())[idx] + parameter_dict = parameters[key] + sim = build_model(key, **parameter_dict) + bud_obj = write_and_run_model(sim, key, silent=silent) + + return key, bud_obj + + +# nosetest - exclude block from this nosetest to the next nosetest +def test_01(): + # Compare model runs with and without viscosity package active + # Model 1 - no viscosity, but with elevated K's that mimic viscosity + # adjusted K values. + modname1, bnd_scen1_bud = scenario(0) + + # Model 2 - include viscosity + modname2, bnd_scen2_bud = scenario(1) + + # Model 3 - no viscosity with basecase K, flows should be reduced + modname3, bnd_scen3_bud = scenario(2) + + # Flow through model should be the same in models 1 & 2 based on + # specified K's. That is, the second model's adjusted K should + # be equal to the pre-specified K in model 1. The third model uses + # the base K of 864 m/d without simulating viscosity effects and + # should result in less flow through the model. + confirm_run_results( + modname1, + modname2, + modname3, + bnd_scen1_bud, + bnd_scen2_bud, + bnd_scen3_bud, + ) + + +# nosetest end + +if __name__ == "__main__": + # Compare model runs with and without viscosity package active + # Model 1 - no viscosity, but with elevated K's that mimic viscosity + # adjusted K values. + modname1, bnd_scen1_bud = scenario(0) + + # Model 2 - include viscosity + modname2, bnd_scen2_bud = scenario(1) + + # Model 3 - no viscosity with basecase K, flows should be reduced + modname3, bnd_scen3_bud = scenario(2) + + # Flow through model should be the same in models 1 & 2 based on + # specified K's. That is, the second model's adjusted K should + # be equal to the pre-specified K in model 1. The third model uses + # the base K of 864 m/d without simulating viscosity effects and + # should result in less flow through the model. + confirm_run_results( + modname1, + modname2, + modname3, + bnd_scen1_bud, + bnd_scen2_bud, + bnd_scen3_bud, + ) diff --git a/autotest/test_gwf_vsc03_sfr.py b/autotest/test_gwf_vsc03_sfr.py new file mode 100644 index 00000000000..8f258b2ff15 --- /dev/null +++ b/autotest/test_gwf_vsc03_sfr.py @@ -0,0 +1,512 @@ +# Scenario envisioned by this test is a river running through a V-shaped +# valley that loses water to the aquifer at the upper end until it goes +# dry, then begins to gain flow again at the lower reaches. River water +# enters the simulation at 8 deg C. Aquifer water starts out at 35 deg C. +# Reference viscosity temperature is 20 deg C. With the VSC package active, +# the simulation should predict less loss of river water to the aquifer +# and more discharge of gw to the stream, compared to the same simulation +# with the VSC package inactive. + +import sys +import math +from io import StringIO +import os +import shutil +import numpy as np +from subprocess import check_output +import flopy + +# Append to system path to include the common subdirectory + +sys.path.append(os.path.join("..", "common")) + +# Import common functionality + +import config +from figspecs import USGSFigure + +mf6exe = os.path.abspath(config.mf6_exe) + +# Setup scenario input +parameters = { + "no-vsc-sfr01": {"viscosity_on": False}, + "vsc-sfr01": {"viscosity_on": True}, +} + +# Equation for determining land surface elevation with a stream running down the middle +def topElev_sfrCentered(x, y): + return ((-0.003 * x) + 260.0) + ( + ((-2e-9 * (x - 5000.0)) + 1e-5) * (y + 1500.0) ** 2 + ) + + +# Model units +length_units = "m" +time_units = "days" + +# model domain and grid definition +Lx = 10000.0 +Ly = 3000.0 +nrow = 60 +ncol = 200 +nlay = 1 +delr = Lx / ncol +delc = Ly / nrow +xmax = ncol * delr +ymax = nrow * delc +X, Y = np.meshgrid( + np.linspace(delr / 2, xmax - delr / 2, ncol), + np.linspace(ymax - delc / 2, 0 + delc / 2, nrow), +) +ibound = np.ones((nlay, nrow, ncol)) +# Because eqn uses negative values in the Y direction, need to do a little manipulation +Y_m = -1 * np.flipud(Y) +top = topElev_sfrCentered(X, Y_m) +botm = np.zeros(top.shape) +strthd = top - 10.0 + +# NPF parameters +k11 = 1 +ss = 0.00001 +sy = 0.20 +hani = 1 +laytyp = 1 + +# Package boundary conditions +viscref = 8.904e-4 + +# time params +steady = {0: True, 1: False} +transient = {0: False, 1: True} +nstp = [1, 20] +tsmult = [1, 1] +perlen = [1, 20] + +nouter, ninner = 1000, 300 +hclose, rclose, relax = 1e-3, 1e-4, 0.97 + +# Transport related parameters +initial_temperature = 35.0 # Initial temperature (unitless) +porosity = 0.20 # porosity (unitless) +K_therm = 2.0 # Thermal conductivity # ($W/m/C$) +rho_water = 1000 # Density of water ($kg/m^3$) +rho_solids = 2650 # Density of the aquifer material ($kg/m^3$) +C_p_w = 4180 # Heat Capacity of water ($J/kg/C$) +C_s = 880 # Heat capacity of the solids ($J/kg/C$) +D_m = K_therm / (porosity * rho_water * C_p_w) +rhob = (1 - porosity) * rho_solids # Bulk density ($kg/m^3$) +K_d = C_s / (rho_water * C_p_w) # Partitioning coefficient ($m^3/kg$) + + +# MODFLOW 6 flopy GWF & GWT simulation object (sim) is returned +# +def build_model(key, viscosity_on=False): + print("Building model...{}".format(key)) + + # Base simulation and model name and workspace + ws = os.path.join("temp", "examples", key) + + # generate names for each model + name = "vsc03" + gwfname = "gwf-" + key + gwtname = "gwt-" + key + + sim = flopy.mf6.MFSimulation(sim_name=name, sim_ws=ws, exe_name=mf6exe) + + tdis_rc = [] + for i in range(len(nstp)): + tdis_rc.append((perlen[i], nstp[i], tsmult[i])) + + flopy.mf6.ModflowTdis( + sim, nper=len(nstp), perioddata=tdis_rc, time_units=time_units + ) + + gwf = flopy.mf6.ModflowGwf( + sim, modelname=gwfname, save_flows=True, newtonoptions="newton" + ) + + ims = flopy.mf6.ModflowIms( + sim, + print_option="ALL", + outer_dvclose=hclose, + outer_maximum=nouter, + under_relaxation="cooley", + inner_maximum=ninner, + inner_dvclose=hclose, + rcloserecord=rclose, + linear_acceleration="BICGSTAB", + scaling_method="NONE", + reordering_method="NONE", + relaxation_factor=relax, + filename="{}.ims".format(gwfname), + ) + sim.register_ims_package(ims, [gwfname]) + + # Instantiate discretization package + flopy.mf6.ModflowGwfdis( + gwf, + length_units=length_units, + nlay=nlay, + nrow=nrow, + ncol=ncol, + delr=delr, + delc=delc, + top=top, + botm=botm, + ) + + # Instantiate node property flow package + flopy.mf6.ModflowGwfnpf( + gwf, + save_specific_discharge=True, + icelltype=1, # >0 means saturated thickness varies with computed head + k=k11, + ) + + # Instantiate storage package + flopy.mf6.ModflowGwfsto( + gwf, + save_flows=False, + iconvert=laytyp, + ss=ss, + sy=sy, + steady_state=steady, + transient=transient, + ) + + # Instantiate initial conditions package + flopy.mf6.ModflowGwfic(gwf, strt=strthd) + + # Instantiate viscosity package + if viscosity_on: + vsc_filerecord = "{}.vsc.bin".format(gwfname) + vsc_pd = [(0, 0.0, 20.0, gwtname, "TEMPERATURE")] + flopy.mf6.ModflowGwfvsc( + gwf, + viscref=viscref, + viscosity_filerecord=vsc_filerecord, + viscosityfuncrecord=[("nonlinear", 10.0, 248.37, 133.15)], + nviscspecies=len(vsc_pd), + packagedata=vsc_pd, + pname="vsc", + filename="{}.vsc".format(gwfname), + ) + + # Instantiate output control package + flopy.mf6.ModflowGwfoc( + gwf, + budget_filerecord=f"{gwfname}.cbc", + head_filerecord=f"{gwfname}.hds", + headprintrecord=[("COLUMNS", 10, "WIDTH", 15, "DIGITS", 6, "GENERAL")], + saverecord=[("HEAD", "ALL"), ("BUDGET", "ALL")], + printrecord=[("HEAD", "ALL"), ("BUDGET", "LAST")], + ) + + # Instantiate recharge package + # total inflow 2000.0 on each side (4,000 total) + rech = np.zeros_like(top) + rech_rate_lo = 0.001 + rech_rate_hi = 0.015 + for i in np.arange(ncol): + rech[0, i] = rech_rate_lo + (rech_rate_hi - rech_rate_lo) / ncol * i + + rech[-1, :] = rech[0, :] + irch = np.zeros_like(rech) + irch = irch.astype(int) + temperature_array = np.ones_like(irch) * 15.0 + aux = {0: [temperature_array]} + flopy.mf6.ModflowGwfrcha( + gwf, + print_flows=True, + recharge=rech, + irch=irch, + auxiliary=["TEMPERATURE"], + aux=aux, + pname="RCHA-1", + filename="{}.rcha".format(gwfname), + ) + + # Instantiate evapotranspiration package + # ET rate is 0.003 everywhere in the model + evtr_lo = 0.0001 + evtr_hi = 0.012 + extdp_hi = 30 + extdp_lo = 10 + evtspd = [] + for i in np.arange(nrow): + for j in np.arange(ncol): + evtr = evtr_hi - (evtr_hi - evtr_lo) / ncol * j + extdp = extdp_hi - (extdp_hi - extdp_lo) / ncol * j + # cellid, surface, rate, depth, [pxdp], [petm], [petm0], [aux] + evtspd.append([(0, i, j), top[i, j], evtr, extdp, 1.0, 0.0]) + surf_rate_specified = True + flopy.mf6.ModflowGwfevt( + gwf, + print_flows=False, + surf_rate_specified=surf_rate_specified, + maxbound=nrow * ncol, + nseg=1, + stress_period_data=evtspd, + auxiliary="TEMPERATURE", + pname="EVT-1", + filename="{}.evt".format(gwfname), + ) + + # Instantiate streamflow routing package + + # Determine the middle row and store in rMid (account for 0-base) + rMid = nrow // 2 - 1 + # sfr data + nreaches = ncol + rlen = delr + rwid = 7.0 + roughness = 0.035 + rbth = 1.0 + rhk = 1.0 + strm_up = 254.899750 + strm_dn = 225.150250 + slope = (strm_up - strm_dn) / ((ncol - 1) * delr) + ustrf = 1.0 + ndv = 0 + strm_incision = 10 + viscaux = 1.111111111 + temperatureaux = 8.0 + + packagedata = [] + for irch in range(nreaches): + nconn = 1 + if 0 < irch < nreaches - 1: + nconn += 1 + rp = [ + irch, + (0, rMid, irch), + rlen, + rwid, + slope, + top[rMid, irch] - strm_incision, + rbth, + rhk, + roughness, + nconn, + ustrf, + ndv, + viscaux, + temperatureaux, + ] + packagedata.append(rp) + + connectiondata = [] + for irch in range(nreaches): + rc = [irch] + if irch > 0: + rc.append(irch - 1) + if irch < nreaches - 1: + rc.append(-(irch + 1)) + connectiondata.append(rc) + + inflow_loc = 0 + sfr_perioddata = [ + [inflow_loc, "inflow", 25000.0], + ] + sfr_perioddata = {0: sfr_perioddata} + + budpth = f"{gwfname}.sfr.cbc" + flopy.mf6.ModflowGwfsfr( + gwf, + save_flows=True, + print_stage=True, + print_flows=True, + print_input=False, + auxiliary=["VDUMMY", "TEMPERATURE"], + unit_conversion=1.486 * 86400, + budget_filerecord=budpth, + mover=False, + nreaches=nreaches, + packagedata=packagedata, + connectiondata=connectiondata, + perioddata=sfr_perioddata, + pname="SFR-1", + filename="{}.sfr".format(gwfname), + ) + + # Setup the GWT model for simulating heat transport + gwt = flopy.mf6.ModflowGwt(sim, modelname=gwtname) + imsgwt = flopy.mf6.ModflowIms( + sim, + print_option="ALL", + outer_dvclose=hclose, + outer_maximum=nouter, + under_relaxation="NONE", + inner_maximum=ninner, + inner_dvclose=hclose, + rcloserecord=rclose, + linear_acceleration="BICGSTAB", + scaling_method="NONE", + reordering_method="NONE", + relaxation_factor=relax, + filename="{}.ims".format(gwtname), + ) + sim.register_ims_package(imsgwt, [gwtname]) + flopy.mf6.ModflowGwtdis( + gwt, + length_units=length_units, + nlay=nlay, + nrow=nrow, + ncol=ncol, + delr=delr, + delc=delc, + top=top, + botm=botm, + ) + + # Instantiate Mobile Storage and Transfer package + flopy.mf6.ModflowGwtmst( + gwt, + porosity=porosity, + sorption="linear", + bulk_density=rhob, + distcoef=K_d, + pname="MST-1", + filename="{}.mst".format(gwtname), + ) + + # Instantiate Transport Initial Conditions package + flopy.mf6.ModflowGwtic(gwt, strt=initial_temperature) + + # Instantiate Advection package + flopy.mf6.ModflowGwtadv(gwt, scheme="UPSTREAM") + + # Instantiate Dispersion package (also handles conduction) + flopy.mf6.ModflowGwtdsp(gwt, xt3d_off=True, diffc=D_m) + + # Instantiate Source/Sink Mixing package + sourcerecarray = [ + ("RCHA-1", "AUX", "TEMPERATURE"), + ("EVT-1", "AUX", "TEMPERATURE"), + ] + flopy.mf6.ModflowGwtssm(gwt, sources=sourcerecarray) + + # Instantiate Streamflow Transport package + sftpackagedata = [] + for irno in range(ncol): + t = (irno, 0.0) + sftpackagedata.append(t) + + sftperioddata = [(0, "STATUS", "CONSTANT"), (0, "CONCENTRATION", 8.0)] + + flopy.mf6.modflow.ModflowGwtsft( + gwt, + boundnames=False, + save_flows=True, + print_input=False, + print_flows=True, + print_concentration=True, + concentration_filerecord=gwtname + ".sft.bin", + budget_filerecord=gwtname + ".sft.bud", + packagedata=sftpackagedata, + reachperioddata=sftperioddata, + flow_package_auxiliary_name="TEMPERATURE", + flow_package_name="SFR-1", + pname="SFT-1", + filename="{}.sft".format(gwtname), + ) + + # Instantiate Output Control package for transport + flopy.mf6.ModflowGwtoc( + gwt, + concentration_filerecord="{}.ucn".format(gwtname), + saverecord=[("CONCENTRATION", "ALL")], + printrecord=[("CONCENTRATION", "LAST"), ("BUDGET", "LAST")], + filename="{}.oc".format(gwtname), + ) + + # Instantiate Gwf-Gwt Exchange package + flopy.mf6.ModflowGwfgwt( + sim, + exgtype="GWF6-GWT6", + exgmnamea=gwfname, + exgmnameb=gwtname, + filename="{}.gwfgwt".format(gwtname), + ) + + return sim + + +# Function to write model files +def write_and_run_model(sim, key, silent=True): + sim.write_simulation(silent=silent) + + success, buff = sim.run_simulation(silent=False) + errmsg = f"simulation did not terminate successfully\n{buff}" + assert success, errmsg + + # slurp in sfr cell-by-cell budgets + simpath = sim.simulation_data.mfpath.get_sim_path() + + sfrbud = "gwf-" + key + ".sfr.cbc" + fpth = os.path.join(simpath, sfrbud) + budobj = flopy.utils.CellBudgetFile(fpth, precision="double") + bud = budobj.get_data(text=" GWF") + + return bud[-1] + + +def scenario(idx, silent=True): + # Two model runs to check relative flows w/ and w/o VSC + + # Model Run 1 (Do not account for the effects of viscosity) + # Model Run 2 (Account for the effects of viscosity) + # --------------------------------------------------------- + key = list(parameters.keys())[idx] + parameter_dict = parameters[key] + sim = build_model(key, **parameter_dict) + sfr_rbr_bud = write_and_run_model(sim, key, silent=silent) + + return sfr_rbr_bud + + +def confirm_run_results(no_vsc_bud, with_vsc_bud): + # sum up total losses and total gains in the first 10 reaches + # and the last 10 reaches + for i in np.arange(10): + # upper reaches + assert abs(no_vsc_bud[i][2]) > abs( + with_vsc_bud[i][2] + ), "GW/SW not as expected" + + # lower reaches + assert abs(no_vsc_bud[-(i + 1)][2]) < abs( + with_vsc_bud[-(i + 1)][2] + ), "GW/SW not as expected" + + +def test_01(): + # Compare model runs with and without viscosity package active + # Model 1 - no viscosity + sfr_scen1_bud = scenario(0, silent=False) + + # Model 2 - include viscosity + sfr_scen2_bud = scenario(1, silent=False) + + # Seepage from upper SFR reaches should decrease owing to cold + # stream water. GW discharge back to lower SFR reaches should + # increase owing to hot groundwater + confirm_changes(sfr_scen1_bud, sfr_scen2_bud) + + +# nosetest end + +if __name__ == "__main__": + # ### V-shaped, linear river valley model + + # Compare model runs with and without viscosity package active + # Model 1 - no viscosity + sfr_scen1_bud = scenario(0) + + # Model 2 - include viscosity + sfr_scen2_bud = scenario(1) + + # Seepage from upper SFR reaches should decrease owing to cold + # stream water. GW discharge back to lower SFR reaches should + # increase owing to hot groundwater + confirm_run_results(sfr_scen1_bud, sfr_scen2_bud) diff --git a/autotest/test_gwf_vsc04_lak.py b/autotest/test_gwf_vsc04_lak.py new file mode 100644 index 00000000000..82a8f7276ea --- /dev/null +++ b/autotest/test_gwf_vsc04_lak.py @@ -0,0 +1,722 @@ +# Simple single lake model. Lake cut into top two layers of a 5 layer +# model. Model is loosely based on the first example problem in +# Merritt and Konikow (2000) which also is one of the MT3D-USGS test +# problems. This test developed to isolate lake-aquifer interaction; +# no SFR or other advanced packages. Problem set up to have groundwater +# pass through the lake: gw inflow on the left side, gw outflow on the +# right side of the lake. Uses constant stage boundary in the lake to +# ensure desired flow conditions for testing budget changes with and +# without VSC active. +# +# starting groundwater temperature: 30.0 +# left chd boundary inflow temperature: 30.0 +# starting lake temperature: 4.0 +# + +import os +import sys + +import numpy as np +import pytest + +try: + import flopy +except: + msg = "Error. FloPy package is not available.\n" + msg += "Try installing using the following command:\n" + msg += " pip install flopy" + raise Exception(msg) + +from framework import testing_framework +import config + +mf6exe = os.path.abspath(config.mf6_exe) + +# Setup scenario input +parameters = { + "no-vsc04-lak": {"viscosity_on": False}, + "vsc04-lak": {"viscosity_on": True}, +} + +# Model units +length_units = "m" +time_units = "days" + +# model domain and grid definition +delr = [ + 76.2, + 304.8, + 304.8, + 304.8, + 304.8, + 304.8, + 152.4, + 152.4, + 152.4, + 152.4, + 152.4, + 304.8, + 304.8, + 304.8, + 304.8, + 304.8, + 76.2, +] + +delc = [ + 76.2, + 304.8, + 304.8, + 304.8, + 304.8, + 304.8, + 152.4, + 152.4, + 152.4, + 152.4, + 152.4, + 304.8, + 304.8, + 304.8, + 304.8, + 304.8, + 76.2, +] + +fixedstrthds = [ + 35.052, + 34.9267, + 34.7216, + 34.5062, + 34.2755, + 34.0237, + 33.8143, + 33.6657, + 33.5077, + 33.3394, + 33.1599, + 32.8728, + 32.4431, + 31.9632, + 31.4353, + 30.8627, + 30.48, +] + +nrow = len(delc) +ncol = len(delr) +top = np.ones((nrow, ncol)) * 35.6616 +bot1 = np.ones_like(top) * 32.6136 +bot2 = np.ones_like(top) * 29.5656 +bot3 = np.ones_like(top) * 26.5176 +bot4 = np.ones_like(top) * 23.4696 +bot5 = np.ones_like(top) * 20.4216 +botm = np.array([bot1, bot2, bot3, bot4, bot5]) +nlay = botm.shape[0] +ibound = np.ones_like(botm) + +# deactive gw cells where lake cells are active +ibound[0, 6:11, 6:11] = 0 # layer 1 +ibound[1, 7:10, 7:10] = 0 # layer 2 + +strthd = np.zeros_like(ibound) +for j in np.arange(ncol): + strthd[:, :, j] = fixedstrthds[j] + +# setup lake array +lakibnd = np.zeros_like(ibound) +lakibnd[0] = 1 - ibound[0] # layer 1 +lakibnd[1] = 1 - ibound[1] # layer 2 + +# NPF parameters +k11 = 9.144 # = 30 ft/day +k33 = 0.9144 # = 30 ft/day +ss = 3e-4 +sy = 0.20 +hani = 1 +laytyp = 1 + +# Package boundary conditions +chdl = 35.052 +chdr = 30.48 +viscref = 8.904e-4 + +# time params +transient = {0: True} +nstp = [100] +tsmult = [1.02] +perlen = [5000] + +# solver params +nouter, ninner = 1000, 300 +hclose, rclose, relax = 1e-3, 1e-4, 0.97 + +# Transport related parameters +al = 1 # longitudinal dispersivity ($m$) +ath1 = al # horizontal transverse dispersivity +atv = al # vertical transverse dispersivity +mixelm = 0 # Upstream vs TVD (Upstream selected) +initial_temperature = 35.0 # Initial temperature (unitless) +porosity = 0.20 # porosity (unitless) +K_therm = 2.0 # Thermal conductivity # ($W/m/C$) +rho_water = 1000 # Density of water ($kg/m^3$) +rho_solids = 2650 # Density of the aquifer material ($kg/m^3$) +C_p_w = 4180 # Heat Capacity of water ($J/kg/C$) +C_s = 880 # Heat capacity of the solids ($J/kg/C$) +D_m = K_therm / (porosity * rho_water * C_p_w) +rhob = (1 - porosity) * rho_solids # Bulk density ($kg/m^3$) +K_d = C_s / (rho_water * C_p_w) # Partitioning coefficient ($m^3/kg$) +leftTemp = 30.0 # Temperature of inflow from left constant head ($C$) + +# Viscosity related parameters +tviscref = 20.0 + +# MODFLOW 6 flopy GWF & GWT simulation object (sim) is returned +# +def build_model(key, viscosity_on=False): + global lak_lkup_dict + print("Building model...{}".format(key)) + + # Base simulation and model name and workspace + ws = os.path.join("temp", "examples", key) + + # generate names for each model + name = "vsc04" + gwfname = "gwf-" + key + gwtname = "gwt-" + key + + sim = flopy.mf6.MFSimulation(sim_name=name, sim_ws=ws, exe_name=mf6exe) + + tdis_rc = [] + for i in range(len(nstp)): + tdis_rc.append((perlen[i], nstp[i], tsmult[i])) + + flopy.mf6.ModflowTdis( + sim, nper=len(nstp), perioddata=tdis_rc, time_units=time_units + ) + + gwf = flopy.mf6.ModflowGwf( + sim, modelname=gwfname, save_flows=True, newtonoptions="newton" + ) + + ims = flopy.mf6.ModflowIms( + sim, + print_option="ALL", + outer_dvclose=hclose, + outer_maximum=nouter, + under_relaxation="cooley", + inner_maximum=ninner, + inner_dvclose=hclose, + rcloserecord=rclose, + linear_acceleration="BICGSTAB", + scaling_method="NONE", + reordering_method="NONE", + relaxation_factor=relax, + filename="{}.ims".format(gwfname), + ) + sim.register_ims_package(ims, [gwfname]) + + # Instantiate discretization package + flopy.mf6.ModflowGwfdis( + gwf, + length_units=length_units, + nlay=nlay, + nrow=nrow, + ncol=ncol, + delr=delr, + delc=delc, + top=top, + botm=botm, + idomain=ibound, + filename="{}.dis".format(gwfname), + ) + + # Instantiate node property flow package + flopy.mf6.ModflowGwfnpf( + gwf, + save_specific_discharge=True, + icelltype=1, # >0 means saturated thickness varies with computed head + k=k11, + k33=k33, + ) + + # Instantiate storage package + flopy.mf6.ModflowGwfsto( + gwf, + save_flows=False, + iconvert=laytyp, + ss=ss, + sy=sy, + transient=transient, + ) + + # Instantiate initial conditions package + flopy.mf6.ModflowGwfic(gwf, strt=strthd) + + # Instantiate viscosity package + if viscosity_on: + vsc_filerecord = "{}.vsc.bin".format(gwfname) + vsc_pd = [(0, 0.0, tviscref, gwtname, "TEMPERATURE")] + flopy.mf6.ModflowGwfvsc( + gwf, + viscref=viscref, + viscosity_filerecord=vsc_filerecord, + viscosityfuncrecord=[("nonlinear", 10.0, 248.37, 133.15)], + nviscspecies=len(vsc_pd), + packagedata=vsc_pd, + pname="vsc", + filename="{}.vsc".format(gwfname), + ) + + # Instantiate output control package + flopy.mf6.ModflowGwfoc( + gwf, + budget_filerecord=f"{gwfname}.cbc", + head_filerecord=f"{gwfname}.hds", + headprintrecord=[("COLUMNS", 17, "WIDTH", 15, "DIGITS", 6, "GENERAL")], + saverecord=[("HEAD", "ALL"), ("BUDGET", "ALL")], + printrecord=[("HEAD", "ALL"), ("BUDGET", "LAST")], + ) + + # Instantiate constant head package + # (for driving gw flow from left to right) + chdlistl = [] + chdlistr = [] + for k in np.arange(nlay): + for i in np.arange(nrow): + # left side + if botm[k, i, 0] <= chdl: + chdlistl.append([(k, i, 0), chdl, leftTemp]) + # right side + if botm[k, i, -1] <= chdr: + chdlistr.append([(k, i, ncol - 1), chdr, 10.0]) + + flopy.mf6.ModflowGwfchd( + gwf, + stress_period_data=chdlistl, + print_input=True, + print_flows=True, + save_flows=False, + pname="CHD-L", + auxiliary="TEMPERATURE", + filename=f"{gwfname}.left.chd", + ) + + flopy.mf6.ModflowGwfchd( + gwf, + stress_period_data=chdlistr, + print_input=True, + print_flows=True, + save_flows=False, + pname="CHD-R", + auxiliary="TEMPERATURE", + filename=f"{gwfname}.right.chd", + ) + + # Instantiate lake package + lakeconnectiondata = [] + nlakecon = [0] # Expand this to [0, 0, ...] for each additional lake + ilakconn = -1 + lak_leakance = 0.1 + lak_lkup_dict = {} + for k in [0, 1]: + for i in range(nrow): + for j in range(ncol): + if lakibnd[k, i, j] == 0: + continue + else: + ilak = int(lakibnd[k, i, j] - 1) + # back + if i > 0: + if ( + lakibnd[k, i - 1, j] == 0 + and ibound[k, i - 1, j] == 1 + ): + ilakconn += 1 + # by setting belev==telev, MF6 will automatically + # re-assign elevations based on cell dimensions + h = [ + ilak, # + ilakconn, # + (k, i - 1, j), # + "horizontal", # + lak_leakance, # + 0.0, # + 0.0, # + delc[i] / 2.0, # + delr[j], # + ] + lakeconnectiondata.append(h) + lak_lkup_dict.update({ilakconn: (k, i, j)}) + + # left + if j > 0: + if ( + lakibnd[k, i, j - 1] == 0 + and ibound[k, i, j - 1] == 1 + ): + ilakconn += 1 + h = [ + ilak, + ilakconn, + (k, i, j - 1), + "horizontal", + lak_leakance, + 0.0, + 0.0, + delr[j] / 2.0, + delc[i], + ] + lakeconnectiondata.append(h) + lak_lkup_dict.update({ilakconn: (k, i, j)}) + + # right + if j < ncol - 1: + if ( + lakibnd[k, i, j + 1] == 0 + and ibound[k, i, j + 1] == 1 + ): + ilakconn += 1 + h = [ + ilak, + ilakconn, + (k, i, j + 1), + "horizontal", + lak_leakance, + 0.0, + 0.0, + delr[j] / 2.0, + delc[i], + ] + lakeconnectiondata.append(h) + lak_lkup_dict.update({ilakconn: (k, i, j)}) + + # front + if i < nrow - 1: + if ( + lakibnd[k, i + 1, j] == 0 + and ibound[k, i + 1, j] == 1 + ): + ilakconn += 1 + h = [ + ilak, + ilakconn, + (k, i + 1, j), + "horizontal", + lak_leakance, + 0.0, + 0.0, + delc[i] / 2.0, + delr[j], + ] + lakeconnectiondata.append(h) + lak_lkup_dict.update({ilakconn: (k, i, j)}) + + # vertical + if lakibnd[k, i, j] == 1 and ibound[k + 1, i, j] == 1: + ilakconn += 1 + v = [ + ilak, + ilakconn, + (k + 1, i, j), + "vertical", + lak_leakance, + 0.0, + 0.0, + 0.0, + 0.0, + ] + lakeconnectiondata.append(v) + lak_lkup_dict.update({ilakconn: (k, i, j)}) + + strtStg = 33.75 + lakpackagedata = [[0, strtStg, len(lakeconnectiondata), 4.0, "lake1"]] + lak_pkdat_dict = {"filename": "lak_pakdata.in", "data": lakpackagedata} + + lakeperioddata = { + 0: [ + (0, "STATUS", "CONSTANT"), # RAINFALL 0.005 & 0.00504739035 + (0, "STAGE", 33.5), + ] + } + + lak_obs = { + "{}.lakeobs".format(gwfname): [ + ("lakestage", "stage", "lake1"), + ("gwexchng", "lak", "lake1"), + ] + } + lak = flopy.mf6.ModflowGwflak( + gwf, + auxiliary="TEMPERATURE", + time_conversion=86400.0, + print_stage=True, + print_flows=True, + budget_filerecord=gwfname + ".lak.bud", + length_conversion=1.0, + mover=False, + pname="LAK-1", + boundnames=True, + nlakes=len(lakpackagedata), + noutlets=0, + packagedata=lak_pkdat_dict, + connectiondata=lakeconnectiondata, + perioddata=lakeperioddata, + observations=lak_obs, + filename="{}.lak".format(gwfname), + ) + + # pull in th etabfile defining the lake stage, vol, & surface area + fname = os.path.join("data", "vsc04-laktab", "stg-vol-surfarea.csv") + tabinput = [] + with open(fname, "r") as f: + # peel off the hdr line + hdr = next(f) + for line in f: + m_arr = line.strip().split(",") + # , , , + tabinput.append([float(m_arr[0]), m_arr[1], m_arr[2]]) + + tab6_filename = "{}.laktab".format(gwfname) + flopy.mf6.ModflowUtllaktab( + gwf, + nrow=len(tabinput), + ncol=3, + table=tabinput, + filename=tab6_filename, + pname="LAK_tab", + parent_file=lak, + ) + + # create gwt model + # ---------------- + gwt = flopy.mf6.ModflowGwt( + sim, modelname=gwtname, model_nam_file="{}.nam".format(gwtname) + ) + gwt.name_file.save_flows = True + + imsgwt = flopy.mf6.ModflowIms( + sim, + print_option="ALL", + outer_dvclose=hclose, + outer_maximum=nouter, + under_relaxation="NONE", + inner_maximum=ninner, + inner_dvclose=hclose, + rcloserecord=rclose, + linear_acceleration="BICGSTAB", + scaling_method="NONE", + reordering_method="NONE", + relaxation_factor=relax, + filename=f"{gwtname}.ims", + ) + sim.register_ims_package(imsgwt, [gwt.name]) + + # Instantiating MODFLOW 6 transport discretization package + flopy.mf6.ModflowGwtdis( + gwt, + nlay=nlay, + nrow=nrow, + ncol=ncol, + delr=delr, + delc=delc, + top=top, + botm=botm, + idomain=ibound, + filename="{}.dis".format(gwtname), + ) + + # Instantiating MODFLOW 6 transport initial concentrations + strtconc = leftTemp + flopy.mf6.ModflowGwtic( + gwt, strt=strtconc, filename="{}.ic".format(gwtname) + ) + + # Instantiate mobile storage and transfer package + sto = flopy.mf6.ModflowGwtmst( + gwt, porosity=porosity, filename=f"{gwtname}.sto" + ) + + # Instantiating MODFLOW 6 transport advection package + if mixelm == 0: + scheme = "UPSTREAM" + elif mixelm == -1: + scheme = "TVD" + else: + raise Exception() + flopy.mf6.ModflowGwtadv( + gwt, scheme=scheme, filename="{}.adv".format(gwtname) + ) + + # Instantiate dispersion package + flopy.mf6.ModflowGwtdsp( + gwt, alh=al, ath1=ath1, atv=atv, filename="{}.dsp".format(gwtname) + ) + + # Instantiate source/sink mixing package + sourcerecarray = [ + ("CHD-L", "AUX", "TEMPERATURE"), + ("CHD-R", "AUX", "TEMPERATURE"), + ] + flopy.mf6.ModflowGwtssm( + gwt, sources=sourcerecarray, filename=f"{gwtname}.ssm" + ) + + # Instantiating MODFLOW 6 transport output control package + flopy.mf6.ModflowGwtoc( + gwt, + budget_filerecord="{}.cbc".format(gwtname), + concentration_filerecord="{}.ucn".format(gwtname), + concentrationprintrecord=[ + ("COLUMNS", 17, "WIDTH", 15, "DIGITS", 6, "GENERAL") + ], + saverecord=[("CONCENTRATION", "ALL"), ("BUDGET", "ALL")], + printrecord=[("CONCENTRATION", "ALL"), ("BUDGET", "ALL")], + filename="{}.oc".format(gwtname), + ) + + # Instantiating MODFLOW 6 lake transport (lkt) package + lktpackagedata = [(0, 4.0, "lake1")] + + lktperioddata = {0: [(0, "STATUS", "CONSTANT"), (0, "CONCENTRATION", 4.0)]} + + # note: for specifying lake number, use fortran indexing! + lkt_obs = { + "{}.lakobs".format(gwtname): [ + ("resTemp", "concentration", 1), + ("resGwMassExchng", "lkt", "lake1"), + ] + } + + flopy.mf6.ModflowGwtlkt( + gwt, # Set time_conversion for use with Manning's eqn. + flow_package_name="LAK-1", + flow_package_auxiliary_name="TEMPERATURE", + budget_filerecord=gwtname + ".lkt.bud", + boundnames=True, + save_flows=True, + print_input=True, + print_flows=False, + print_concentration=True, + packagedata=lktpackagedata, + lakeperioddata=lktperioddata, + observations=lkt_obs, + pname="LKT-1", + filename="{}.lkt".format(gwtname), + ) + + # GWF GWT exchange + flopy.mf6.ModflowGwfgwt( + sim, + exgtype="GWF6-GWT6", + exgmnamea=gwfname, + exgmnameb=gwtname, + filename=f"{name}.gwfgwt", + ) + + return sim + + +def write_and_run_model(sim, key, silent=True): + sim.write_simulation(silent=silent) + + success, buff = sim.run_simulation(silent=False) + errmsg = f"simulation did not terminate successfully\n{buff}" + assert success, errmsg + + # slurp in sfr cell-by-cell budgets + simpath = sim.simulation_data.mfpath.get_sim_path() + + lakbud = "gwf-" + key + ".lak.bud" + fpth = os.path.join(simpath, lakbud) + budobj = flopy.utils.CellBudgetFile(fpth, precision="double") + bud = budobj.get_data(text=" GWF") + + return bud[-1] + + +def scenario(idx, silent=True): + # Two model runs to check relative flows w/ and w/o VSC + + # Model Run 1 (Do not account for the effects of viscosity) + # Model Run 2 (Account for the effects of viscosity) + # --------------------------------------------------------- + key = list(parameters.keys())[idx] + parameter_dict = parameters[key] + sim = build_model(key, **parameter_dict) + lak_cbc_bud = write_and_run_model(sim, key, silent=silent) + + return lak_cbc_bud + + +def confirm_run_results(no_vsc_bud, with_vsc_bud): + # talley some flows on the left and right sides of the lake for comparison + # test + no_vsc_bud_np = np.array(no_vsc_bud.tolist()) + with_vsc_bud_np = np.array(with_vsc_bud.tolist()) + + left_chk_no_vsc = [] + right_chk_no_vsc = [] + left_chk_with_vsc = [] + right_chk_with_vsc = [] + + for idx in np.arange(no_vsc_bud_np.shape[0]): + k, i, j = lak_lkup_dict[idx] + + # left side of lake + if j < 7: + if no_vsc_bud_np[idx, 2] > 0 and with_vsc_bud_np[idx, 2] > 0: + left_chk_no_vsc.append(no_vsc_bud_np[idx, 2]) + left_chk_with_vsc.append(with_vsc_bud_np[idx, 2]) + + # right side of lake + if j > 9: + if no_vsc_bud_np[idx, 2] < 0 and with_vsc_bud_np[idx, 2] < 0: + right_chk_no_vsc.append(no_vsc_bud_np[idx, 2]) + right_chk_with_vsc.append(with_vsc_bud_np[idx, 2]) + + # Check that all the flows entering the lak in the 'with vsc' model are greater + # than their 'no vsc' counterpart + assert np.greater( + np.array(left_chk_with_vsc), np.array(left_chk_no_vsc) + ).all(), "Lake inflow did no increase with VSC turned on and should have." + + # Check that all the flows leaving the lak in the 'with vsc' model are less + # than their 'no vsc' counterpart (keep in mind values are negative, which + # affects how the comparison is made) + assert np.greater( + np.array(right_chk_with_vsc), np.array(right_chk_no_vsc) + ).all(), "Lake outflow did no decrease with VSC turned on and should have." + + +# nosetest - exclude block from this nosetest to the next nosetest +def test_01(): + # Compare model runs with and without viscosity package active + # Model 1 - no viscosity + lak_scen1_bud = scenario(0, silent=False) + + # Model 2 - include viscosity + lak_scen2_bud = scenario(1, silent=False) + + # GW discharge into left side of lake should increase owing to hot + # groundwater. GW discharge out of the right side of the lake should + # increase owing to cold lake water. + confirm_run_results(lak_scen1_bud, lak_scen2_bud) + + +# nosetest end + +if __name__ == "__main__": + # ### Adapted from Merritt & Konikow (2000) example problem 1. + + # Compare model runs with and without viscosity package active + # Model 1 - no viscosity + lak_scen1_bud = scenario(0) + + # Model 2 - include viscosity + lak_scen2_bud = scenario(1) + + # GW discharge into left side of lake should increase owing to hot + # groundwater. GW discharge out of the right side of the lake should + # increase owing to cold lake water. + confirm_run_results(lak_scen1_bud, lak_scen2_bud) diff --git a/doc/MODFLOW6References.bib b/doc/MODFLOW6References.bib index db6303c1e6e..ebd845d7ab0 100644 --- a/doc/MODFLOW6References.bib +++ b/doc/MODFLOW6References.bib @@ -494,7 +494,7 @@ @book{Voss1984sutra Date-Added = {2019-08-29 15:00:00 -0600}, Date-Modified = {2019-08-29 15:00:00 -0600}, Series = {{U.S. Geological Survey Water-Resources Investigations Report 84--4369, 409 p.}}, - Title = {SUTRA---a finite-element simulation model for saturated-unsaturated fluid-density-dependent ground-water flow with energy transport or chemically-reactive single-species solute transport}, + Title = {SUTRA---A finite-element simulation model for saturated-unsaturated fluid-density-dependent ground-water flow with energy transport or chemically-reactive single-species solute transport}, Year = {1984}} @article{VossSouza1987, @@ -597,6 +597,34 @@ @book{langevin2002seawat Year = {2002}, Bdsk-Url-1 = {https://pubs.er.usgs.gov/publication/tm6A22}} +@article{diersch2002viscosity, + Author = {Diersch, Hans-Jorg G and Kolditz, Olaf}, + Date-Added = {2022-09-27 11:58:19 -0500}, + Date-Modified = {2022-09-27 12:00:01 -0500}, + Journal = {Advances in Water Resources}, + Pages = {899--944}, + Title = {Variable-density flow and transport in porous media: Approaches and challenges}, + Url = {https://www.sciencedirect.com/science/article/pii/S0309170802000635}, + doi = {https://doi.org/10.1016/S0309-1708(02)00063-5}, + Urldate = {September 27, 2022}, + Volume = {25}, + number = {8}, + Year = {2002}, + issn = {0309-1708}, + Bdsk-Url-1 = {https://pubs.er.usgs.gov/publication/tm6A22}} + +@book{kipp1987, + Author = {Kipp, Kenneth L}, + Date-Added = {2022-09-27 11:58:19 -0500}, + Date-Modified = {2022-09-27 12:00:01 -0500}, + Institution = {U.S. Geological Survey}, + Series = {{U.S. Geological Survey Water-Resources Investigation Report 86-4095, 517 p.}}, + Title = {HST3D: A Computer Code for Simulation of Heat and Solute Transport in Three-Dimensional Ground-Water Flow Systems}, + Url = {https://pubs.usgs.gov/wri/1986/4095/report.pdf}, + Urldate = {September 27, 2022}, + Year = {1987}, + Bdsk-Url-1 = {https://pubs.er.usgs.gov/publication/wri864095}} + @book{langevin2008seawat, Author = {Langevin, Christian D and Thorne Jr, Daniel T and Dausman, Alyssa M and Sukop, Michael C and Guo, Weixing}, Date-Added = {2019-07-25 11:53:50 -0500}, @@ -2855,3 +2883,38 @@ @book{vs2d Urldate = {June 27, 2017}, Year = {1987}, Bdsk-Url-1 = {https://pubs.er.usgs.gov/publication/wri834099}} + +@book{healy1996, + Author = {Healy, Richard W and Ronan, Ann D}, + Date-Added = {2022-09-27 11:58:19 -0500}, + Date-Modified = {2022-09-27 12:00:01 -0500}, + Institution = {U.S. Geological Survey}, + Series = {{U.S. Geological Survey Water-Resources Investigation Report 96-4230, 36 p.}}, + Title = {Documentation of Computer Program VS2DH for Simulation of Energy Transport in Variably Saturated Porous Media: Modification of the U.S. Geological Survey's Computer Program VS2DT}, + Doi = {10.3133/wri964230}, + Url = {https://pubs.usgs.gov/wri/1996/4230/report.pdf}, + Urldate = {September 27, 2022}, + Year = {1996}, + Bdsk-Url-1 = {https://pubs.er.usgs.gov/publication/wri964230}} + +@book{hughes2004, + Author = {Hughes, Joseph D and Sanford, Ward E}, + Date-Added = {2019-10-11 15:42:15 -0400}, + Date-Modified = {2019-10-11 15:46:29 -0400}, + Doi = {10.3133/ofr20041207}, + Url = {https://water.usgs.gov/nrp/gwsoftware/SutraMS/OFR2004-1207.pdf}, + Series = {{U.S. Geological Survey Open File Report 2004--1207, 141 p.}}, + Title = {SUTRA-MS: A version of SUTRA modified to simulate heat and multiple-solute transport}, + Urldate = {September 27, 2022}, + Year = {2004}, + Bdsk-Url-1 = {https://pubs.er.usgs.gov/publication/ofr20041207}} + +@book{maidment1993, + Author = {Maidment, David R}, + Date-Added = {2022-10-11 15:42:15 -0400}, + Date-Modified = {2022-10-11 15:46:29 -0400}, + Isbn = {0-07-039732-5}, + Publisher = {McGraw-Hill}, + Address = {New York, USA}, + Title = {Handbook of Hydrology}, + Year = {1993}} diff --git a/doc/ReleaseNotes/ReleaseNotes.tex b/doc/ReleaseNotes/ReleaseNotes.tex index 7394311db4a..871409faa3b 100644 --- a/doc/ReleaseNotes/ReleaseNotes.tex +++ b/doc/ReleaseNotes/ReleaseNotes.tex @@ -192,17 +192,17 @@ \section{Changes Introduced in this Release} \underline{NEW FUNCTIONALITY} \begin{itemize} - \item xxx + \item A new Viscosity (VSC) package for the Groundwater Flow (GWF) Model is introduced in this release. The effects of viscosity are accounted for by updates intercell conductance, as well as the conductance between the aquifer and head-dependent boundaries, based on simulated concentrations and\/or temperatures. As with other packages, the viscosity package is activated by specifying ``VSC6'' as the file type in a GWF name file. Testing of the VSC Package is accomplished using example test problems; however, changes to the code and input may be required in response to user needs and testing. % \item xxx % \item xxx \end{itemize} - %\underline{EXAMPLES} - %\begin{itemize} - % \item xxx + \underline{EXAMPLES} + \begin{itemize} + \item Four new example problems added to the ``autotest'' folder that tests the functionality of the new Viscosity (VSC) Package and named test\_gwf\_vsc01, test\_gwf\_vsc02, test\_gwf\_vsc03\_sfr, and test\_gwf\_vsc04\_lak % \item xxx % \item xxx - %\end{itemize} + \end{itemize} \textbf{\underline{BUG FIXES AND OTHER CHANGES TO EXISTING FUNCTIONALITY}} \\ \underline{BASIC FUNCTIONALITY} diff --git a/doc/mf6io/gwf/gwf.tex b/doc/mf6io/gwf/gwf.tex index 98cd9b37554..452122446ef 100644 --- a/doc/mf6io/gwf/gwf.tex +++ b/doc/mf6io/gwf/gwf.tex @@ -109,6 +109,10 @@ \subsection{Skeletal Storage, Compaction, and Subsidence (CSUB) Package} \subsection{Buoyancy (BUY) Package} \input{gwf/buy} +\newpage +\subsection{Viscosity (VSC) Package} +\input{gwf/vsc} + \newpage \subsection{Constant-Head (CHD) Package} \input{gwf/chd} diff --git a/doc/mf6io/gwf/namefile.tex b/doc/mf6io/gwf/namefile.tex index 4d8724801ca..f54f1ee1e5f 100644 --- a/doc/mf6io/gwf/namefile.tex +++ b/doc/mf6io/gwf/namefile.tex @@ -31,6 +31,7 @@ \subsubsection{Explanation of Variables} STO6 & Storage Package \\ CSUB6 & Compaction and Subsidence Package \\ BUY6 & Buoyancy Package \\ +VSC6 & Viscosity Package \\ HFB6 & Horizontal Flow Barrier Package\\ CHD6 & Time-Variant Specified Head Option & * \\ WEL6 & Well Package & * \\ diff --git a/doc/mf6io/gwf/vsc.tex b/doc/mf6io/gwf/vsc.tex new file mode 100644 index 00000000000..1a1e41270cc --- /dev/null +++ b/doc/mf6io/gwf/vsc.tex @@ -0,0 +1,73 @@ +Input to the Viscosity (VSC) Package is read from the file that has type ``VSC6'' in the Name File. If the VSC Package is active within a groundwater flow model, then the model will account for the dependence of fluid viscosity on solute concentration and the resulting changes in hydraulic conductivity and stress-package conductances, which vary inversely with viscosity. Viscosity can be calculated as a function of one or more groundwater solute transport (GWT) species using an approach described in the Supplemental Technical Information document distributed with MODFLOW 6 (Chapter 8). Only one VSC Package can be specified for a GWF model. The VSC Package can be coupled with one or more GWT Models so that the fluid viscosity is updated dynamically with one or more simulated concentration fields. + +The VSC Package calculates fluid viscosity using the following equation from \cite{langevin2008seawat}: + +\begin{equation} +\label{eqn:visclinear} +\mu = VISCREF + \sum_{i=1}^{NVISCSPECIES} DVISCDC_i \left ( CONCENTRATION_i - CVISCREF_i \right ) +\end{equation} + +\noindent where $\mu$ is the calculated viscosity, $VISCREF$ is the viscosity of a reference fluid, typically taken to be freshwater at a temperature of 20 degrees Celsius, $NVISCSPECIES$ is the number of chemical species that contribute to the viscosity calculation, $DVISCDC_i$ is the parameter that describes how viscosity changes linearly as a function of concentration for chemical species $i$ (i.e. the slope of a line that relates viscosity to concentration), $CONCENTRATION_i$ is the concentration of species $i$, and $CVISCREF_i$ is the reference concentration for species $i$ corresponding to when the viscosity of the reference fluid is equal to $VISCREF$, which is normally set to a concentration of zero. + +In many applications, variations in temperature have a greater effect on fluid viscosity than variations in solute concentration. When a GWT model is formulated such that one of the transported ``species'' is heat (thermal energy), with ``concentration'' used to represent temperature \citep{zheng2010supplemental}, the viscosity can vary linearly with temperature, as it can with any other ``concentration.'' In that case, $CONCENTRATION_i$ and $CVISCREF_i$ represent the simulated and reference temperatures, respectively, and $DVISCDC_i$ represents the rate at which viscosity changes with temperature. In addition, the viscosity formula can optionally include a nonlinear dependence on temperature. In that case, equation 3 becomes + +\begin{equation} +\label{eqn:viscnonlinear} +\mu = \mu_T(T) + \sum_{i=1}^{NVISCSPECIES} DVISCDC_i \left ( CONCENTRATION_i - CVISCREF_i \right ) +\end{equation} + +where the first term on the right-hand side, $\mu_T(T)$, is a nonlinear function of temperature, and the summation corresponds to the summation in equation \ref{eqn:visclinear}, in which one of the ``species'' is heat. The nonlinear term in equation \ref{eqn:viscnonlinear} is of the form + +\begin{equation} +\label{eqn:munonlinear} +\mu_T(T) = CVISCREF_i \cdot A_2^{\left [ \frac {-A_3 \left ( CONCENTRATION_i - CVISCREF_i \right ) } {\left ( CONCENTRATION_i + A_4 \right ) \left ( CVISCREF_i + A_4 \right )} \right ]} +\end{equation} + +\noindent where the coefficients $A_2$, $A_3$, and $A_4$ are specified by the user. Values for $A_2$, $A_3$, and $A_4$ are commonly 10, 248.7, and 133.15, respectively \citep{langevin2008seawat, Voss1984sutra}. + +\subsubsection{Stress Packages} + +For head-dependent stress packages, the VSC Package can adjust the conductance used to calculate flow between the boundary and the aquifer to account for variations in viscosity. Conductance is assumed to vary inversely with viscosity. + +By default, the boundary viscosity is set equal to VISCREF, which, for freshwater, is typically set equal to 1.0. However, there are two additional options for setting the viscosity of a boundary package. The first is to assign an auxiliary variable with the name ``VISCOSITY''. If an auxiliary variable named ``VISCOSITY'' is detected, then it will be assigned as the viscosity of the fluid entering from the boundary. Alternatively, a viscosity value can be calculated for each boundary using the viscosity equation described above and one or more concentrations provided as auxiliary variables. In this case, the user must assign one auxiliary variable for each AUXSPECIESNAME listed in the PACKAGEDATA block below. Thus, there must be NVISCSPECIES auxiliary variables, each with the identical name as those specified in PACKAGEDATA. The VSC Package will calculate the viscosity for each boundary using these concentrations and the values specified for VISCREF, DVISCDC, and CVISCREF. If the boundary package contains an auxiliary variable named VISCOSITY and also contains AUXSPECIESNAME auxiliary variables, then the boundary viscosity value will be assigned to the one in the VISCOSITY auxiliary variable. + +A GWT Model can be used to calculate concentrations for the advanced stress packages (LAK, SFR, MAW, and UZF) if corresponding advanced transport packages are specified (LKT, SFT, MWT, and UZT). The advanced stress packages have an input option called FLOW\_PACKAGE\_AUXILIARY\_NAME. When activated, this option will result in the simulated concentration for a lake or other feature being copied from the advanced transport package into the auxiliary variable for the corresponding GWF stress package. This means that the viscosity for a lake or stream, for example, can be dynamically updated during the simulation using concentrations from advanced transport packages that are fed into auxiliary variables in the advanced stress packages, and ultimately used by the VSC Package to calculate a fluid viscosity. This concept also applies when multiple GWT Models are used simultaneously to simulate multiple species. In this case, multiple auxiliary variables are required for an advanced stress package, with each one representing a concentration from a different GWT Model. + + +\begin{longtable}{p{3cm} p{12cm}} +\caption{Description of viscosity terms for stress packages} +\tabularnewline +\hline +\hline +\textbf{Stress Package} & \textbf{Note} \\ +\hline +\endhead +\hline +\endfoot +GHB & A VISCOSITY auxiliary variable or one or more auxiliary variables for calculating viscosity in the equation of state can be specified \\ +RIV & A VISCOSITY auxiliary variable or one or more auxiliary variables for calculating viscosity in the equation of state can be specified \\ +DRN & The drain formulation assumes that the drain boundary contains water of the same viscosity as the discharging water; auxiliary variables have no effect on the drain calculation \\ +LAK & A VISCOSITY auxiliary variable or one or more auxiliary variables for calculating viscosity in the equation of state can be specified \\ +SFR & A VISCOSITY auxiliary variable or one or more auxiliary variables for calculating viscosity in the equation of state can be specified \\ +MAW & A VISCOSITY auxiliary variable or one or more auxiliary variables for calculating viscosity in the equation of state can be specified \\ +UZF & Pending ... \\ +\end{longtable} + +\vspace{5mm} +\subsubsection{Structure of Blocks} + +\vspace{5mm} +\noindent \textit{FOR EACH SIMULATION} +\lstinputlisting[style=blockdefinition]{./mf6ivar/tex/gwf-vsc-options.dat} +\lstinputlisting[style=blockdefinition]{./mf6ivar/tex/gwf-vsc-dimensions.dat} +\lstinputlisting[style=blockdefinition]{./mf6ivar/tex/gwf-vsc-packagedata.dat} + +\vspace{5mm} +\subsubsection{Explanation of Variables} +\begin{description} +\input{./mf6ivar/tex/gwf-vsc-desc.tex} +\end{description} + +\vspace{5mm} +\subsubsection{Example Input File} +\lstinputlisting[style=inputfile]{./mf6ivar/examples/gwf-vsc-example.dat} diff --git a/doc/mf6io/mf6ivar/dfn/gwf-vsc.dfn b/doc/mf6io/mf6ivar/dfn/gwf-vsc.dfn new file mode 100644 index 00000000000..4633076b027 --- /dev/null +++ b/doc/mf6io/mf6ivar/dfn/gwf-vsc.dfn @@ -0,0 +1,185 @@ +# --------------------- gwf vsc options --------------------- + +block options +name viscref +type double +reader urword +optional true +longname reference viscosity +description fluid reference viscosity used in the equation of state. This value is set to 1.0 if not specified as an option. +default_value 1.0 + +block options +name viscosity_filerecord +type record viscosity fileout viscosityfile +shape +reader urword +tagged true +optional true +longname +description + +block options +name viscosity +type keyword +shape +in_record true +reader urword +tagged true +optional false +longname viscosity keyword +description keyword to specify that record corresponds to viscosity. + +block options +name fileout +type keyword +shape +in_record true +reader urword +tagged true +optional false +longname file keyword +description keyword to specify that an output filename is expected next. + +block options +name viscosityfile +type string +preserve_case true +shape +in_record true +reader urword +tagged false +optional false +longname file keyword +description name of the binary output file to write viscosity information. The viscosity file has the same format as the head file. Viscosity values will be written to the viscosity file whenever heads are written to the binary head file. The settings for controlling head output are contained in the Output Control option. + +block options +name viscosityfuncrecord +type record thermal_viscosity_func formulation a2 a3 a4 +reader urword +optional true +longname +description + +block options +name thermal_viscosity_func +type keyword +shape +in_record true +tagged true +reader urword +optional false +longname keyword to specify viscosity formulation for the temperature species +description may be used for specifying which viscosity formulation to use for a species identified by the auxilary name TEMPERATURE. Can be either LINEAR or NONLINEAR. The LINEAR viscosity formulation is the default. + +block options +name formulation +type string +reader urword +optional false +valid linear nonlinear +longname keyword to specify viscosity formulation +description may be used for specifying which viscosity formulation to use. The linear viscosity formulation is the default. + + +block options +name a2 +type double precision +in_record true +reader urword +optional true +longname coefficient used in nonlinear viscosity function +description is an empirical parameter specified by the user for calculating viscosity using a nonlinear formulation. If A2 is not specified, a default value of 10.0 is assigned (Voss, 1984). + +block options +name a3 +type double precision +in_record true +reader urword +optional true +longname coefficient used in nonlinear viscosity function +description is an empirical parameter specified by the user for calculating viscosity using a nonlinear formulation. If A3 is not specified, a default value of 248.37 is assigned (Voss, 1984). + +block options +name a4 +type double precision +in_record true +reader urword +optional true +longname coefficient used in nonlinear viscosity function +description is an empirical parameter specified by the user for calculating viscosity using a nonlinear formulation. If A4 is not specified, a default value of 133.15 is assigned (Voss, 1984). + + + +# --------------------- gwf vsc dimensions --------------------- + +block dimensions +name nviscspecies +type integer +reader urword +optional false +longname number of species used in viscosity equation of state +description number of species used in the viscosity equation of state. If either concentrations or temperature (or both) are used to update viscosity then then nrhospecies needs to be at least one. + + +# --------------------- gwf vsc packagedata --------------------- + +block packagedata +name packagedata +type recarray iviscspec dviscdc cviscref modelname auxspeciesname +shape (nrhospecies) +reader urword +longname +description + +block packagedata +name iviscspec +type integer +shape +tagged false +in_record true +reader urword +longname species number for this entry +description integer value that defines the species number associated with the specified PACKAGEDATA data entered on each line. IVISCSPECIES must be greater than zero and less than or equal to NVISCSPECIES. Information must be specified for each of the NVISCSPECIES species or the program will terminate with an error. The program will also terminate with an error if information for a species is specified more than once. +numeric_index true + +block packagedata +name dviscdc +type double precision +shape +tagged false +in_record true +reader urword +longname slope of the line that defines the linear relationship between viscosity and temperature or between viscosity and concentration, depending on the type of species entered on each line. +description real value that defines the slope of the line defining the linear relationship between viscosity and temperature or between viscosity and concentration, depending on the type of species entered on each line. If the value of AUXSPECIESNAME entered on a line is TEMPERATURE, this value will be used when VISCOSITY\_FUNC is equal to LINEAR (the default) in the OPTIONS block. When VISCOSITY\_FUNC is set to NONLINEAR, a value for DVISCDC must be specified though it is not used. + +block packagedata +name cviscref +type double precision +shape +tagged false +in_record true +reader urword +longname reference temperature value or reference concentration value +description real value that defines the reference temperature or reference concentration value used for this species in the viscosity equation of state. If AUXSPECIESNAME entered on a line is TEMPERATURE, then CVISCREF refers to a reference temperature, otherwise it refers to a reference concentration. + +block packagedata +name modelname +type string +in_record true +tagged false +shape +reader urword +longname modelname +description name of a GWT (or eventuallky a GWE) model used to simulate a species that will be used in the viscosity equation of state. This name will have no effect if the simulation does not include a GWT model that corresponds to this GWF model. + +block packagedata +name auxspeciesname +type string +in_record true +tagged false +shape +reader urword +longname auxspeciesname +description name of an auxiliary variable in a GWF stress package that will be used for this species to calculate the viscosity values. If a viscosity value is needed by the Viscosity Package then it will use the temperature or concentration values associated with this AUXSPECIESNAME in the viscosity equation of state. For advanced stress packages (LAK, SFR, MAW, and UZF) that have an associated advanced transport package (LKT, SFT, MWT, and UZT), the FLOW\_PACKAGE\_AUXILIARY\_NAME option in the advanced transport package can be used to transfer simulated temperature or concentration(s) into the flow package auxiliary variable. In this manner, the Viscosity Package can calculate viscosity values for lakes, streams, multi-aquifer wells, and unsaturated zone flow cells using simulated concentrations. + diff --git a/doc/mf6io/mf6ivar/examples/gwf-vsc-example.dat b/doc/mf6io/mf6ivar/examples/gwf-vsc-example.dat new file mode 100644 index 00000000000..ace2f4cb7c3 --- /dev/null +++ b/doc/mf6io/mf6ivar/examples/gwf-vsc-example.dat @@ -0,0 +1,15 @@ +BEGIN OPTIONS + VISCREF 8.904E-04 + VISCOSITY FILEOUT GWF-VSC.vsc.bin + THERMAL_VISCOSITY_FUNC NONLINEAR 10.0 248.37 133.15 +END OPTIONS + +BEGIN DIMENSIONS + NVISCSPECIES 2 +END DIMENSIONS + +BEGIN PACKAGEDATA +# ISPEC DVISCDC CVISCREF MODELNAME AUXSPECIESNAME + 1 1.92e-6 0.0 GWT-SALT SALINITY + 2 0.00 25.0 GWT-TEMP TEMPERATURE +END PACKAGEDATA diff --git a/doc/mf6io/mf6ivar/md/mf6ivar.md b/doc/mf6io/mf6ivar/md/mf6ivar.md index 172ce2b91ed..685a1ed5e7b 100644 --- a/doc/mf6io/mf6ivar/md/mf6ivar.md +++ b/doc/mf6io/mf6ivar/md/mf6ivar.md @@ -147,9 +147,9 @@ | GWF | DISV | DIMENSIONS | NLAY | INTEGER | is the number of layers in the model grid. | | GWF | DISV | DIMENSIONS | NCPL | INTEGER | is the number of cells per layer. This is a constant value for the grid and it applies to all layers. | | GWF | DISV | DIMENSIONS | NVERT | INTEGER | is the total number of (x, y) vertex pairs used to characterize the horizontal configuration of the model grid. | -| GWF | DISV | GRIDDATA | TOP | DOUBLE PRECISION (NCPL) | is the top elevation for each cell in the top model layer. | -| GWF | DISV | GRIDDATA | BOTM | DOUBLE PRECISION (NLAY, NCPL) | is the bottom elevation for each cell. | -| GWF | DISV | GRIDDATA | IDOMAIN | INTEGER (NLAY, NCPL) | is an optional array that characterizes the existence status of a cell. If the IDOMAIN array is not specified, then all model cells exist within the solution. If the IDOMAIN value for a cell is 0, the cell does not exist in the simulation. Input and output values will be read and written for the cell, but internal to the program, the cell is excluded from the solution. If the IDOMAIN value for a cell is 1 or greater, the cell exists in the simulation. If the IDOMAIN value for a cell is -1, the cell does not exist in the simulation. Furthermore, the first existing cell above will be connected to the first existing cell below. This type of cell is referred to as a ``vertical pass through'' cell. | +| GWF | DISV | GRIDDATA | TOP | DOUBLE PRECISION (NCPL, 1) | is the top elevation for each cell in the top model layer. | +| GWF | DISV | GRIDDATA | BOTM | DOUBLE PRECISION (NCPL, 1, NLAY) | is the bottom elevation for each cell. | +| GWF | DISV | GRIDDATA | IDOMAIN | INTEGER (NCPL, 1, NLAY) | is an optional array that characterizes the existence status of a cell. If the IDOMAIN array is not specified, then all model cells exist within the solution. If the IDOMAIN value for a cell is 0, the cell does not exist in the simulation. Input and output values will be read and written for the cell, but internal to the program, the cell is excluded from the solution. If the IDOMAIN value for a cell is 1 or greater, the cell exists in the simulation. If the IDOMAIN value for a cell is -1, the cell does not exist in the simulation. Furthermore, the first existing cell above will be connected to the first existing cell below. This type of cell is referred to as a ``vertical pass through'' cell. | | GWF | DISV | VERTICES | IV | INTEGER | is the vertex number. Records in the VERTICES block must be listed in consecutive order from 1 to NVERT. | | GWF | DISV | VERTICES | XV | DOUBLE PRECISION | is the x-coordinate for the vertex. | | GWF | DISV | VERTICES | YV | DOUBLE PRECISION | is the y-coordinate for the vertex. | @@ -187,6 +187,7 @@ | GWF | DISU | CELL2D | ICVERT | INTEGER (NCVERT) | is an array of integer values containing vertex numbers (in the VERTICES block) used to define the cell. Vertices must be listed in clockwise order. | | GWF | IC | GRIDDATA | STRT | DOUBLE PRECISION (NODES) | is the initial (starting) head---that is, head at the beginning of the GWF Model simulation. STRT must be specified for all simulations, including steady-state simulations. One value is read for every model cell. For simulations in which the first stress period is steady state, the values used for STRT generally do not affect the simulation (exceptions may occur if cells go dry and (or) rewet). The execution time, however, will be less if STRT includes hydraulic heads that are close to the steady-state solution. A head value lower than the cell bottom can be provided if a cell should start as dry. | | GWF | NPF | OPTIONS | SAVE_FLOWS | KEYWORD | keyword to indicate that budget flow terms will be written to the file specified with ``BUDGET SAVE FILE'' in Output Control. | +| GWF | NPF | OPTIONS | PRINT_FLOWS | KEYWORD | keyword to indicate that calculated flows between cells will be printed to the listing file for every stress period time step in which ``BUDGET PRINT'' is specified in Output Control. If there is no Output Control option and ``PRINT\_FLOWS'' is specified, then flow rates are printed for the last time step of each stress period. This option can produce extremely large list files because all cell-by-cell flows are printed. It should only be used with the NPF Package for models that have a small number of cells. | | GWF | NPF | OPTIONS | ALTERNATIVE_CELL_AVERAGING | STRING | is a text keyword to indicate that an alternative method will be used for calculating the conductance for horizontal cell connections. The text value for ALTERNATIVE\_CELL\_AVERAGING can be ``LOGARITHMIC'', ``AMT-LMK'', or ``AMT-HMK''. ``AMT-LMK'' signifies that the conductance will be calculated using arithmetic-mean thickness and logarithmic-mean hydraulic conductivity. ``AMT-HMK'' signifies that the conductance will be calculated using arithmetic-mean thickness and harmonic-mean hydraulic conductivity. If the user does not specify a value for ALTERNATIVE\_CELL\_AVERAGING, then the harmonic-mean method will be used. This option cannot be used if the XT3D option is invoked. | | GWF | NPF | OPTIONS | THICKSTRT | KEYWORD | indicates that cells having a negative ICELLTYPE are confined, and their cell thickness for conductance calculations will be computed as STRT-BOT rather than TOP-BOT. | | GWF | NPF | OPTIONS | VARIABLECV | KEYWORD | keyword to indicate that the vertical conductance will be calculated using the saturated thickness and properties of the overlying cell and the thickness and properties of the underlying cell. If the DEWATERED keyword is also specified, then the vertical conductance is calculated using only the saturated thickness and properties of the overlying cell if the head in the underlying cell is below its top. If these keywords are not specified, then the default condition is to calculate the vertical conductance at the start of the simulation using the initial head and the cell properties. The vertical conductance remains constant for the entire simulation. | @@ -204,7 +205,12 @@ | GWF | NPF | OPTIONS | K33OVERK | KEYWORD | keyword to indicate that specified K33 is a ratio of K33 divided by K. If this option is specified, then the K33 array entered in the NPF Package will be multiplied by K after being read. | | GWF | NPF | OPTIONS | TVK6 | KEYWORD | keyword to specify that record corresponds to a time-varying hydraulic conductivity (TVK) file. The behavior of TVK and a description of the input file is provided separately. | | GWF | NPF | OPTIONS | FILEIN | KEYWORD | keyword to specify that an input filename is expected next. | -| GWF | NPF | OPTIONS | TVK_FILENAME | STRING | defines a time-varying hydraulic conductivity (TVK) input file. Records in the TVK file can be used to change hydraulic conductivity properties at specified times or stress periods. | +| GWF | NPF | OPTIONS | TVK6_FILENAME | STRING | defines a time-varying hydraulic conductivity (TVK) input file. Records in the TVK file can be used to change hydraulic conductivity properties at specified times or stress periods. | +| GWF | NPF | OPTIONS | DEV_NO_NEWTON | KEYWORD | turn off Newton for unconfined cells | +| GWF | NPF | OPTIONS | DEV_MODFLOWUSG_UPSTREAM_WEIGHTED_SATURATION | KEYWORD | use MODFLOW-USG upstream-weighted saturation approach | +| GWF | NPF | OPTIONS | DEV_MODFLOWNWT_UPSTREAM_WEIGHTING | KEYWORD | use MODFLOW-NWT approach for upstream weighting | +| GWF | NPF | OPTIONS | DEV_MINIMUM_SATURATED_THICKNESS | DOUBLE PRECISION | set minimum allowed saturated thickness | +| GWF | NPF | OPTIONS | DEV_OMEGA | DOUBLE PRECISION | set saturation omega value | | GWF | NPF | GRIDDATA | ICELLTYPE | INTEGER (NODES) | flag for each cell that specifies how saturated thickness is treated. 0 means saturated thickness is held constant; $>$0 means saturated thickness varies with computed head when head is below the cell top; $<$0 means saturated thickness varies with computed head unless the THICKSTRT option is in effect. When THICKSTRT is in effect, a negative value of icelltype indicates that saturated thickness will be computed as STRT-BOT and held constant. | | GWF | NPF | GRIDDATA | K | DOUBLE PRECISION (NODES) | is the hydraulic conductivity. For the common case in which the user would like to specify the horizontal hydraulic conductivity and the vertical hydraulic conductivity, then K should be assigned as the horizontal hydraulic conductivity, K33 should be assigned as the vertical hydraulic conductivity, and K22 and the three rotation angles should not be specified. When more sophisticated anisotropy is required, then K corresponds to the K11 hydraulic conductivity axis. All included cells (IDOMAIN $>$ 0) must have a K value greater than zero. | | GWF | NPF | GRIDDATA | K22 | DOUBLE PRECISION (NODES) | is the hydraulic conductivity of the second ellipsoid axis (or the ratio of K22/K if the K22OVERK option is specified); for an unrotated case this is the hydraulic conductivity in the y direction. If K22 is not included in the GRIDDATA block, then K22 is set equal to K. For a regular MODFLOW grid (DIS Package is used) in which no rotation angles are specified, K22 is the hydraulic conductivity along columns in the y direction. For an unstructured DISU grid, the user must assign principal x and y axes and provide the angle for each cell face relative to the assigned x direction. All included cells (IDOMAIN $>$ 0) must have a K22 value greater than zero. | @@ -785,6 +791,21 @@ | GWF | OC | PERIOD | LAST | KEYWORD | keyword to indicate save for last step in period. This keyword may be used in conjunction with other keywords to print or save results for multiple time steps. | | GWF | OC | PERIOD | FREQUENCY | INTEGER | save at the specified time step frequency. This keyword may be used in conjunction with other keywords to print or save results for multiple time steps. | | GWF | OC | PERIOD | STEPS | INTEGER ( +END DIMENSIONS diff --git a/doc/mf6io/mf6ivar/tex/gwf-vsc-options.dat b/doc/mf6io/mf6ivar/tex/gwf-vsc-options.dat new file mode 100644 index 00000000000..09bf3a99fd9 --- /dev/null +++ b/doc/mf6io/mf6ivar/tex/gwf-vsc-options.dat @@ -0,0 +1,6 @@ +BEGIN OPTIONS + [VISCREF ] + [VISCOSITY FILEOUT ] + [THERMAL_VISCOSITY_FUNC FORMULATION [A2 ] [A3 ] [A4 ]] + FORMULATION +END OPTIONS diff --git a/doc/mf6io/mf6ivar/tex/gwf-vsc-packagedata.dat b/doc/mf6io/mf6ivar/tex/gwf-vsc-packagedata.dat new file mode 100644 index 00000000000..ff3d65886f9 --- /dev/null +++ b/doc/mf6io/mf6ivar/tex/gwf-vsc-packagedata.dat @@ -0,0 +1,5 @@ +BEGIN PACKAGEDATA + + + ... +END PACKAGEDATA diff --git a/msvs/mf6core.vfproj b/msvs/mf6core.vfproj index 975d2eb3bd0..a117490ee66 100644 --- a/msvs/mf6core.vfproj +++ b/msvs/mf6core.vfproj @@ -105,6 +105,7 @@ + @@ -135,6 +136,7 @@ + diff --git a/src/Exchange/GwfGwtExchange.f90 b/src/Exchange/GwfGwtExchange.f90 index b8a121c474f..44e040b5986 100644 --- a/src/Exchange/GwfGwtExchange.f90 +++ b/src/Exchange/GwfGwtExchange.f90 @@ -275,12 +275,18 @@ subroutine exg_ar(this) end if end if ! - ! -- Set a pointer to conc + ! -- Set a pointer to conc in buy if (gwfmodel%inbuy > 0) then call gwfmodel%buy%set_concentration_pointer(gwtmodel%name, gwtmodel%x, & gwtmodel%ibound) end if ! + ! -- Set a pointer to conc (which could be a temperature) in vsc + if (gwfmodel%invsc > 0) then + call gwfmodel%vsc%set_concentration_pointer(gwtmodel%name, gwtmodel%x, & + gwtmodel%ibound) + end if + ! ! -- transfer the boundary package information from gwf to gwt call this%gwfbnd2gwtfmi() ! diff --git a/src/Model/Connection/GwfInterfaceModel.f90 b/src/Model/Connection/GwfInterfaceModel.f90 index a4dfe220e1a..8735a32c9e8 100644 --- a/src/Model/Connection/GwfInterfaceModel.f90 +++ b/src/Model/Connection/GwfInterfaceModel.f90 @@ -94,7 +94,7 @@ subroutine gwfifm_df(this) ! define NPF package call npfOptions%construct() call this%setNpfOptions(npfOptions) - call this%npf%npf_df(this%dis, this%xt3d, 0, npfOptions) + call this%npf%npf_df(this%dis, this%xt3d, 0, 0, npfOptions) call npfOptions%destroy() ! define BUY package @@ -119,7 +119,7 @@ end subroutine gwfifm_df subroutine gwfifm_ar(this) class(GwfInterfaceModelType) :: this !< the GWF interface model - call this%npf%npf_ar(this%ic, this%ibound, this%x) + call this%npf%npf_ar(this%ic, this%vsc, this%ibound, this%x) if (this%inbuy > 0) call this%buy%buy_ar(this%npf, this%ibound) end subroutine gwfifm_ar diff --git a/src/Model/GroundWaterFlow/gwf3.f90 b/src/Model/GroundWaterFlow/gwf3.f90 index 9b519ca16d7..2bb74273aa3 100644 --- a/src/Model/GroundWaterFlow/gwf3.f90 +++ b/src/Model/GroundWaterFlow/gwf3.f90 @@ -11,6 +11,7 @@ module GwfModule use GwfNpfModule, only: GwfNpfType use Xt3dModule, only: Xt3dType use GwfBuyModule, only: GwfBuyType + use GwfVscModule, only: GwfVscType use GwfHfbModule, only: GwfHfbType use GwfStoModule, only: GwfStoType use GwfCsubModule, only: GwfCsubType @@ -35,6 +36,7 @@ module GwfModule type(GwfNpfType), pointer :: npf => null() ! node property flow package type(Xt3dType), pointer :: xt3d => null() ! xt3d option for npf type(GwfBuyType), pointer :: buy => null() ! buoyancy package + type(GwfVscType), pointer :: vsc => null() ! viscosity package type(GwfStoType), pointer :: sto => null() ! storage package type(GwfCsubType), pointer :: csub => null() ! subsidence package type(GwfOcType), pointer :: oc => null() ! output control package @@ -47,6 +49,7 @@ module GwfModule integer(I4B), pointer :: inoc => null() ! unit number OC integer(I4B), pointer :: innpf => null() ! unit number NPF integer(I4B), pointer :: inbuy => null() ! unit number BUY + integer(I4B), pointer :: invsc => null() ! unit number VSC integer(I4B), pointer :: insto => null() ! unit number STO integer(I4B), pointer :: incsub => null() ! unit number CSUB integer(I4B), pointer :: inmvr => null() ! unit number MVR @@ -96,7 +99,7 @@ module GwfModule &'GHB6 ', 'RCH6 ', 'EVT6 ', 'OBS6 ', 'GNC6 ', & ! 15 &'API6 ', 'CHD6 ', ' ', ' ', ' ', & ! 20 &' ', 'MAW6 ', 'SFR6 ', 'LAK6 ', 'UZF6 ', & ! 25 - &'DISV6', 'MVR6 ', 'CSUB6', 'BUY6 ', ' ', & ! 30 + &'DISV6', 'MVR6 ', 'CSUB6', 'BUY6 ', 'VSC6 ', & ! 30 &70*' '/ contains @@ -122,6 +125,7 @@ subroutine gwf_cr(filename, id, modelname) use GwfNpfModule, only: npf_cr use Xt3dModule, only: xt3d_cr use GwfBuyModule, only: buy_cr + use GwfVscModule, only: vsc_cr use GwfStoModule, only: sto_cr use GwfCsubModule, only: csub_cr use GwfMvrModule, only: mvr_cr @@ -235,6 +239,7 @@ subroutine gwf_cr(filename, id, modelname) call namefile_obj%get_unitnumber('OC6', this%inoc, 1) call namefile_obj%get_unitnumber('NPF6', this%innpf, 1) call namefile_obj%get_unitnumber('BUY6', this%inbuy, 1) + call namefile_obj%get_unitnumber('VSC6', this%invsc, 1) call namefile_obj%get_unitnumber('STO6', this%insto, 1) call namefile_obj%get_unitnumber('CSUB6', this%incsub, 1) call namefile_obj%get_unitnumber('MVR6', this%inmvr, 1) @@ -261,6 +266,7 @@ subroutine gwf_cr(filename, id, modelname) call npf_cr(this%npf, this%name, this%innpf, this%iout) call xt3d_cr(this%xt3d, this%name, this%innpf, this%iout) call buy_cr(this%buy, this%name, this%inbuy, this%iout) + call vsc_cr(this%vsc, this%name, this%invsc, this%iout) call gnc_cr(this%gnc, this%name, this%ingnc, this%iout) call hfb_cr(this%hfb, this%name, this%inhfb, this%iout) call sto_cr(this%sto, this%name, this%insto, this%iout) @@ -306,10 +312,11 @@ subroutine gwf_df(this) ! ! -- Define packages and utility objects call this%dis%dis_df() - call this%npf%npf_df(this%dis, this%xt3d, this%ingnc) + call this%npf%npf_df(this%dis, this%xt3d, this%ingnc, this%invsc) call this%oc%oc_df() call this%budget%budget_df(niunit, 'VOLUME', 'L**3') if (this%inbuy > 0) call this%buy%buy_df(this%dis) + if (this%invsc > 0) call this%vsc%vsc_df(this%dis) if (this%ingnc > 0) call this%gnc%gnc_df(this) ! ! -- Assign or point model members to dis members @@ -416,7 +423,9 @@ subroutine gwf_ar(this) ! ! -- Allocate and read modules attached to model if (this%inic > 0) call this%ic%ic_ar(this%x) - if (this%innpf > 0) call this%npf%npf_ar(this%ic, this%ibound, this%x) + if (this%innpf > 0) call this%npf%npf_ar(this%ic, this%vsc, this%ibound, & + this%x) + if (this%invsc > 0) call this%vsc%vsc_ar(this%ibound) if (this%inbuy > 0) call this%buy%buy_ar(this%npf, this%ibound) if (this%inhfb > 0) call this%hfb%hfb_ar(this%ibound, this%xt3d, this%dis) if (this%insto > 0) call this%sto%sto_ar(this%dis, this%ibound) @@ -439,6 +448,7 @@ subroutine gwf_ar(this) ! -- Read and allocate package call packobj%bnd_ar() if (this%inbuy > 0) call this%buy%buy_ar_bnd(packobj, this%x) + if (this%invsc > 0) call this%vsc%vsc_ar_bnd(packobj) end do ! ! -- return @@ -466,6 +476,7 @@ subroutine gwf_rp(this) ! -- Read and prepare if (this%innpf > 0) call this%npf%npf_rp() if (this%inbuy > 0) call this%buy%buy_rp() + if (this%invsc > 0) call this%vsc%vsc_rp() if (this%inhfb > 0) call this%hfb%hfb_rp() if (this%inoc > 0) call this%oc%oc_rp() if (this%insto > 0) call this%sto%sto_rp() @@ -515,6 +526,7 @@ subroutine gwf_ad(this) end if ! ! -- Advance + if (this%invsc > 0) call this%vsc%vsc_ad() if (this%innpf > 0) call this%npf%npf_ad(this%dis%nodes, this%xold, & this%x, irestore) if (this%insto > 0) call this%sto%sto_ad() @@ -524,6 +536,7 @@ subroutine gwf_ad(this) do ip = 1, this%bndlist%Count() packobj => GetBndFromList(this%bndlist, ip) call packobj%bnd_ad() + if (this%invsc > 0) call this%vsc%vsc_ad_bnd(packobj, this%x) if (isimcheck > 0) then call packobj%bnd_ck() end if @@ -1123,24 +1136,31 @@ subroutine gwf_ot_dv(this, idvsave, idvprint, ipflag) integer(I4B), intent(inout) :: ipflag class(BndType), pointer :: packobj integer(I4B) :: ip - + ! ! -- Save compaction to binary file if (this%incsub > 0) call this%csub%csub_ot_dv(idvsave, idvprint) - + ! ! -- save density to binary file if (this%inbuy > 0) then call this%buy%buy_ot_dv(idvsave) end if - + ! + ! -- save viscosity to binary file + if (this%invsc > 0) then + call this%vsc%vsc_ot_dv(idvsave) + end if + ! ! -- Print advanced package dependent variables do ip = 1, this%bndlist%Count() packobj => GetBndFromList(this%bndlist, ip) call packobj%bnd_ot_dv(idvsave, idvprint) end do - + ! ! -- save head and print head call this%oc%oc_ot(ipflag) - + ! + ! -- Return + return end subroutine gwf_ot_dv subroutine gwf_ot_bdsummary(this, ibudfl, ipflag) @@ -1207,6 +1227,7 @@ subroutine gwf_da(this) call this%npf%npf_da() call this%xt3d%xt3d_da() call this%buy%buy_da() + call this%vsc%vsc_da() call this%gnc%gnc_da() call this%sto%sto_da() call this%csub%csub_da() @@ -1222,6 +1243,7 @@ subroutine gwf_da(this) deallocate (this%npf) deallocate (this%xt3d) deallocate (this%buy) + deallocate (this%vsc) deallocate (this%gnc) deallocate (this%sto) deallocate (this%csub) @@ -1244,6 +1266,7 @@ subroutine gwf_da(this) call mem_deallocate(this%inobs) call mem_deallocate(this%innpf) call mem_deallocate(this%inbuy) + call mem_deallocate(this%invsc) call mem_deallocate(this%insto) call mem_deallocate(this%incsub) call mem_deallocate(this%inmvr) @@ -1336,6 +1359,7 @@ subroutine allocate_scalars(this, modelname) call mem_allocate(this%inoc, 'INOC', this%memoryPath) call mem_allocate(this%innpf, 'INNPF', this%memoryPath) call mem_allocate(this%inbuy, 'INBUY', this%memoryPath) + call mem_allocate(this%invsc, 'INVSC', this%memoryPath) call mem_allocate(this%insto, 'INSTO', this%memoryPath) call mem_allocate(this%incsub, 'INCSUB', this%memoryPath) call mem_allocate(this%inmvr, 'INMVR', this%memoryPath) @@ -1349,6 +1373,7 @@ subroutine allocate_scalars(this, modelname) this%inoc = 0 this%innpf = 0 this%inbuy = 0 + this%invsc = 0 this%insto = 0 this%incsub = 0 this%inmvr = 0 diff --git a/src/Model/GroundWaterFlow/gwf3drn8.f90 b/src/Model/GroundWaterFlow/gwf3drn8.f90 index 45a329f9faa..665e2e5a0dd 100644 --- a/src/Model/GroundWaterFlow/gwf3drn8.f90 +++ b/src/Model/GroundWaterFlow/gwf3drn8.f90 @@ -1,12 +1,14 @@ module DrnModule - use KindModule, only: DP, I4B + use KindModule, only: DP, I4B, LGP use ConstantsModule, only: DZERO, DONE, DTWO, & - LENFTYPE, LENPACKAGENAME, LENAUXNAME, LINELENGTH - use MemoryHelperModule, only: create_mem_path + LENFTYPE, LENPACKAGENAME, LENAUXNAME, LINELENGTH, & + LENMEMPATH, LENVARNAME, LENMEMSEPARATOR + use MemoryHelperModule, only: create_mem_path, split_mem_address use SmoothingModule, only: sQSaturation, sQSaturationDerivative, & sQuadraticSaturation use BndModule, only: BndType use ObsModule, only: DefaultObsIdProcessor + use TdisModule, only: delt, totimc use TimeSeriesLinkModule, only: TimeSeriesLinkType, & GetTimeSeriesLinkFromList ! @@ -18,6 +20,7 @@ module DrnModule ! character(len=LENFTYPE) :: ftype = 'DRN' character(len=LENPACKAGENAME) :: text = ' DRN' + character(len=LENMEMSEPARATOR), parameter :: memPathSeparator = '/' ! type, extends(BndType) :: DrnType @@ -27,6 +30,7 @@ module DrnModule contains procedure :: allocate_scalars => drn_allocate_scalars procedure :: bnd_options => drn_options + procedure :: bnd_ad => drn_ad procedure :: bnd_ck => drn_ck procedure :: bnd_cf => drn_cf procedure :: bnd_fc => drn_fc @@ -63,6 +67,10 @@ subroutine drn_create(packobj, id, ibcnum, inunit, iout, namemodel, pakname) character(len=*), intent(in) :: pakname ! -- local type(DrnType), pointer :: drnobj + character(len=LENMEMPATH) :: vscpath !< if vsc exist, this is path name + character(len=LENMEMPATH) :: locmempath !< the memory path for the model + character(len=LENVARNAME) :: locvarname !< the package name to check on + logical(LGP) :: vscexists !< flag will be true if vsc is active ! ------------------------------------------------------------------------------ ! ! -- allocate the object and assign values to object variables @@ -88,10 +96,47 @@ subroutine drn_create(packobj, id, ibcnum, inunit, iout, namemodel, pakname) packobj%iscloc = 2 !sfac applies to conductance packobj%ictMemPath = create_mem_path(namemodel, 'NPF') ! + ! -- check if vsc package exists and set flag if so + vscpath = trim(namemodel)//memPathSeparator//'VSC' + call split_mem_address(vscpath, locmempath, locvarname, vscexists) + if (vscexists) then + packobj%ivsc = 1 + end if + ! ! -- return return end subroutine drn_create + !> @ brief Advance the drain boundary package + !! + !! Advance data in the drain boundary package. Overides the bnd_ad() + !! routine in the bndType parent class. The method advances time + !! series and observation data as well as updates the user-specified + !! conductance based on changes in viscosity when water enters from + !! the boundary + !< + subroutine drn_ad(this) + ! -- dummy variables + class(DrnType) :: this !< DrnType object + ! -- local variables + real(DP) :: begintime, endtime + ! + ! -- Initialize time variables + begintime = totimc + endtime = begintime + delt + ! + ! -- Advance the time series managers + call this%TsManager%ad() + call this%TasManager%ad() + ! + ! -- For each observation, push simulated value and corresponding + ! simulation time from "current" to "preceding" and reset + ! "current" value. + call this%obs%obs_ad() + ! + return + end subroutine drn_ad + subroutine drn_da(this) ! ****************************************************************************** ! drn_da -- deallocate diff --git a/src/Model/GroundWaterFlow/gwf3ghb8.f90 b/src/Model/GroundWaterFlow/gwf3ghb8.f90 index 9757b1a16c4..0d94e072ad9 100644 --- a/src/Model/GroundWaterFlow/gwf3ghb8.f90 +++ b/src/Model/GroundWaterFlow/gwf3ghb8.f90 @@ -1,7 +1,8 @@ module ghbmodule - use KindModule, only: DP, I4B - use ConstantsModule, only: DZERO, LENFTYPE, LENPACKAGENAME - use MemoryHelperModule, only: create_mem_path + use KindModule, only: DP, I4B, LGP + use ConstantsModule, only: DZERO, LENFTYPE, LENPACKAGENAME, LENMEMPATH, & + LENVARNAME, LENMEMSEPARATOR + use MemoryHelperModule, only: create_mem_path, split_mem_address use BndModule, only: BndType use ObsModule, only: DefaultObsIdProcessor use TimeSeriesLinkModule, only: TimeSeriesLinkType, & @@ -15,8 +16,10 @@ module ghbmodule ! character(len=LENFTYPE) :: ftype = 'GHB' character(len=LENPACKAGENAME) :: text = ' GHB' + character(len=LENMEMSEPARATOR), parameter :: memPathSeparator = '/' ! type, extends(BndType) :: GhbType + contains procedure :: bnd_options => ghb_options procedure :: bnd_ck => ghb_ck @@ -51,6 +54,10 @@ subroutine ghb_create(packobj, id, ibcnum, inunit, iout, namemodel, pakname) character(len=*), intent(in) :: pakname ! -- local type(GhbType), pointer :: ghbobj + character(len=LENMEMPATH) :: vscpath !< if vsc exist, this is path name + character(len=LENMEMPATH) :: locmempath !< the memory path for the model + character(len=LENVARNAME) :: locvarname !< the package name to check on + logical(LGP) :: vscexists !< flag will be true if vsc is active ! ------------------------------------------------------------------------------ ! ! -- allocate the object and assign values to object variables @@ -75,6 +82,13 @@ subroutine ghb_create(packobj, id, ibcnum, inunit, iout, namemodel, pakname) packobj%iscloc = 2 packobj%ictMemPath = create_mem_path(namemodel, 'NPF') ! + ! -- check if vsc package exists and set flag if so + vscpath = trim(namemodel)//memPathSeparator//'VSC' + call split_mem_address(vscpath, locmempath, locvarname, vscexists) + if (vscexists) then + packobj%ivsc = 1 + end if + ! ! -- return return end subroutine ghb_create diff --git a/src/Model/GroundWaterFlow/gwf3lak8.f90 b/src/Model/GroundWaterFlow/gwf3lak8.f90 index d11275dadbf..7df8ebc4924 100644 --- a/src/Model/GroundWaterFlow/gwf3lak8.f90 +++ b/src/Model/GroundWaterFlow/gwf3lak8.f90 @@ -190,6 +190,9 @@ module LakModule integer(I4B), pointer :: idense real(DP), dimension(:, :), pointer, contiguous :: denseterms => null() ! + ! -- viscosity variables + real(DP), dimension(:, :), pointer, contiguous :: viscratios => null() !< viscosity ratios (1: lak vsc ratio; 2: gwf vsc ratio) + ! ! -- type bound procedures contains procedure :: lak_allocate_scalars @@ -267,6 +270,8 @@ module LakModule ! -- density procedure :: lak_activate_density procedure, private :: lak_calculate_density_exchange + ! -- viscosity + procedure :: lak_activate_viscosity end type LakType contains @@ -375,6 +380,7 @@ subroutine lak_allocate_scalars(this) this%bditems = 11 this%cbcauxitems = 1 this%idense = 0 + this%ivsc = 0 ! ! -- return return @@ -445,6 +451,9 @@ subroutine lak_allocate_arrays(this) ! -- allocate denseterms to size 0 call mem_allocate(this%denseterms, 3, 0, 'DENSETERMS', this%memoryPath) ! + ! -- allocate viscratios to size 0 + call mem_allocate(this%viscratios, 2, 0, 'VISCRATIOS', this%memoryPath) + ! ! -- return return end subroutine lak_allocate_arrays @@ -2338,9 +2347,11 @@ subroutine lak_calculate_conn_conductance(this, ilak, iconn, stage, head, cond) real(DP) :: botl real(DP) :: sat real(DP) :: wa + real(DP) :: vscratio ! -- formats ! ------------------------------------------------------------------------------ cond = DZERO + vscratio = DONE topl = this%telev(iconn) botl = this%belev(iconn) call this%lak_calculate_cond_head(iconn, stage, head, vv) @@ -2370,7 +2381,18 @@ subroutine lak_calculate_conn_conductance(this, ilak, iconn, stage, head, cond) end if sat = wa end if - cond = sat * this%satcond(iconn) + ! + ! -- account for viscosity effects (if vsc active) + if (this%ivsc == 1) then + ! flow from lake to aquifer + if (stage > head) then + vscratio = this%viscratios(1, iconn) + ! flow from aquifer to lake + else if (head > stage) then + vscratio = this%viscratios(2, iconn) + end if + end if + cond = sat * this%satcond(iconn) * vscratio ! ! -- return return @@ -4451,6 +4473,7 @@ subroutine lak_da(this) call mem_deallocate(this%qleak) call mem_deallocate(this%qsto) call mem_deallocate(this%denseterms) + call mem_deallocate(this%viscratios) ! ! -- tables if (this%ntables > 0) then @@ -6361,6 +6384,36 @@ subroutine lak_activate_density(this) return end subroutine lak_activate_density + !> @brief Activate viscosity terms + !! + !! Method to activate addition of viscosity terms for a LAK package reach. + !! + !< + subroutine lak_activate_viscosity(this) + ! -- modules + use MemoryManagerModule, only: mem_reallocate + ! -- dummy variables + class(LakType), intent(inout) :: this !< LakType object + ! -- local variables + integer(I4B) :: i + integer(I4B) :: j + ! + ! -- Set ivsc and reallocate viscratios to be of size MAXBOUND + this%ivsc = 1 + call mem_reallocate(this%viscratios, 2, this%MAXBOUND, 'VISCRATIOS', & + this%memoryPath) + do i = 1, this%maxbound + do j = 1, 2 + this%viscratios(j, i) = DZERO + end do + end do + write (this%iout, '(/1x,a)') 'VISCOSITY HAS BEEN ACTIVATED FOR LAK & + &PACKAGE: '//trim(adjustl(this%packName)) + ! + ! -- return + return + end subroutine lak_activate_viscosity + subroutine lak_calculate_density_exchange(this, iconn, stage, head, cond, & botl, flow, gwfhcof, gwfrhs) ! ****************************************************************************** diff --git a/src/Model/GroundWaterFlow/gwf3maw8.f90 b/src/Model/GroundWaterFlow/gwf3maw8.f90 index 415d79e6d13..f586bf12319 100644 --- a/src/Model/GroundWaterFlow/gwf3maw8.f90 +++ b/src/Model/GroundWaterFlow/gwf3maw8.f90 @@ -158,6 +158,9 @@ module MawModule integer(I4B), pointer :: idense real(DP), dimension(:, :), pointer, contiguous :: denseterms => null() ! + ! -- viscosity variables + real(DP), dimension(:, :), pointer, contiguous :: viscratios => null() !< viscosity ratios (1: maw vsc ratio; 2: gwf vsc ratio) + ! ! -- type bound procedures contains procedure :: maw_allocate_scalars @@ -213,6 +216,8 @@ module MawModule ! -- MAW reduced flow outputs procedure, private :: maw_redflow_csv_init procedure, private :: maw_redflow_csv_write + ! -- viscosity + procedure :: maw_activate_viscosity end type MawType contains @@ -318,6 +323,7 @@ subroutine maw_allocate_scalars(this) this%kappa = DEM4 this%cbcauxitems = 1 this%idense = 0 + this%ivsc = 0 ! ! -- return return @@ -521,6 +527,9 @@ subroutine maw_allocate_well_conn_arrays(this) ! -- allocate denseterms to size 0 call mem_allocate(this%denseterms, 3, 0, 'DENSETERMS', this%memoryPath) ! + ! -- allocate viscratios to size 0 + call mem_allocate(this%viscratios, 2, 0, 'VISCRATIOS', this%memoryPath) + ! ! -- return return end subroutine maw_allocate_well_conn_arrays @@ -3003,6 +3012,7 @@ subroutine maw_da(this) call mem_deallocate(this%qsto) call mem_deallocate(this%qconst) call mem_deallocate(this%denseterms) + call mem_deallocate(this%viscratios) call mem_deallocate(this%idxlocnode) call mem_deallocate(this%idxdglo) call mem_deallocate(this%idxoffdglo) @@ -3033,6 +3043,7 @@ subroutine maw_da(this) call mem_deallocate(this%kappa) call mem_deallocate(this%cbcauxitems) call mem_deallocate(this%idense) + call mem_deallocate(this%viscratios) ! ! -- pointers to gwf variables nullify (this%gwfiss) @@ -3858,10 +3869,12 @@ subroutine maw_calculate_conn_terms(this, n, j, icflow, cmaw, cterm, term, & real(DP) :: hbar real(DP) :: drterm real(DP) :: dhbarterm + real(DP) :: vscratio ! ------------------------------------------------------------------------------ ! ! -- initialize terms cterm = DZERO + vscratio = DONE icflow = 0 if (present(term2)) then inewton = 1 @@ -3877,9 +3890,19 @@ subroutine maw_calculate_conn_terms(this, n, j, icflow, cmaw, cterm, term, & tmaw = this%topscrn(jpos) bmaw = this%botscrn(jpos) ! + ! -- if vsc active, select appropriate viscosity ratio + if (this%ivsc == 1) then + ! flow out of well (flow is negative) + if (flow < 0) then + vscratio = this%viscratios(1, igwfnode) + else if (flow > 0) then + vscratio = this%viscratios(2, igwfnode) + end if + end if + ! ! -- calculate saturation call this%maw_calculate_saturation(n, j, igwfnode, sat) - cmaw = this%satcond(jpos) * sat + cmaw = this%satcond(jpos) * vscratio * sat ! ! -- set upstream head, term, and term2 if returning newton terms if (inewton == 1) then @@ -3928,14 +3951,14 @@ subroutine maw_calculate_conn_terms(this, n, j, icflow, cmaw, cterm, term, & ! -- maw is upstream if (hmaw > hgwf) then hbar = sQuadratic0sp(hgwf, en, this%satomega) - term = drterm * this%satcond(jpos) * (hbar - hmaw) + term = drterm * this%satcond(jpos) * vscratio * (hbar - hmaw) dhbarterm = sQuadratic0spDerivative(hgwf, en, this%satomega) term2 = cmaw * (dhbarterm - DONE) ! ! -- gwf is upstream else hbar = sQuadratic0sp(hmaw, en, this%satomega) - term = -drterm * this%satcond(jpos) * (hgwf - hbar) + term = -drterm * this%satcond(jpos) * vscratio * (hgwf - hbar) dhbarterm = sQuadratic0spDerivative(hmaw, en, this%satomega) term2 = cmaw * (DONE - dhbarterm) end if @@ -3944,7 +3967,7 @@ subroutine maw_calculate_conn_terms(this, n, j, icflow, cmaw, cterm, term, & ! ! -- flow is not corrected, so calculate term for newton formulation if (inewton /= 0) then - term = drterm * this%satcond(jpos) * (hgwf - hmaw) + term = drterm * this%satcond(jpos) * vscratio * (hgwf - hmaw) end if end if ! @@ -4165,20 +4188,32 @@ subroutine maw_calculate_qpot(this, n, qnet) real(DP) :: bmaw real(DP) :: htmp real(DP) :: hv + real(DP) :: vscratio ! -- format ! ------------------------------------------------------------------------------ ! ! -- initialize qnet and htmp qnet = DZERO + vscratio = DONE htmp = this%shutofflevel(n) ! + ! -- if vsc active, select appropriate viscosity ratio + if (this%ivsc == 1) then + ! flow out of well (flow is negative) + if (qnet < 0) then + vscratio = this%viscratios(1, igwfnode) + else if (qnet > 0) then + vscratio = this%viscratios(2, igwfnode) + end if + end if + ! ! -- calculate discharge to flowing wells if (this%iflowingwells > 0) then if (this%fwcond(n) > DZERO) then bt = this%fwelev(n) tp = bt + this%fwrlen(n) scale = sQSaturation(tp, bt, htmp) - cfw = scale * this%fwcond(n) + cfw = scale * this%fwcond(n) * this%viscratios(2, n) this%ifwdischarge(n) = 0 if (cfw > DZERO) then this%ifwdischarge(n) = 1 @@ -4203,7 +4238,7 @@ subroutine maw_calculate_qpot(this, n, qnet) jpos = this%get_jpos(n, j) igwfnode = this%get_gwfnode(n, j) call this%maw_calculate_saturation(n, j, igwfnode, sat) - cmaw = this%satcond(jpos) * sat + cmaw = this%satcond(jpos) * vscratio * sat hgwf = this%xnew(igwfnode) bmaw = this%botscrn(jpos) hv = htmp @@ -4843,6 +4878,36 @@ subroutine maw_activate_density(this) return end subroutine maw_activate_density + !> @brief Activate viscosity terms + !! + !! Method to activate addition of viscosity terms for a MAW package reach. + !! + !< + subroutine maw_activate_viscosity(this) + ! -- modules + use MemoryManagerModule, only: mem_reallocate + ! -- dummy variables + class(MawType), intent(inout) :: this !< MawType object + ! -- local variables + integer(I4B) :: i + integer(I4B) :: j + ! + ! -- Set ivsc and reallocate viscratios to be of size MAXBOUND + this%ivsc = 1 + call mem_reallocate(this%viscratios, 2, this%MAXBOUND, 'VISCRATIOS', & + this%memoryPath) + do i = 1, this%maxbound + do j = 1, 2 + this%viscratios(j, i) = DZERO + end do + end do + write (this%iout, '(/1x,a)') 'VISCOSITY HAS BEEN ACTIVATED FOR MAW & + &PACKAGE: '//trim(adjustl(this%packName)) + ! + ! -- return + return + end subroutine maw_activate_viscosity + subroutine maw_calculate_density_exchange(this, iconn, hmaw, hgwf, cond, & bmaw, flow, hcofterm, rhsterm) ! ****************************************************************************** diff --git a/src/Model/GroundWaterFlow/gwf3npf8.f90 b/src/Model/GroundWaterFlow/gwf3npf8.f90 index dcfdf99bc31..df7b595e381 100644 --- a/src/Model/GroundWaterFlow/gwf3npf8.f90 +++ b/src/Model/GroundWaterFlow/gwf3npf8.f90 @@ -11,6 +11,7 @@ module GwfNpfModule use GwfNpfOptionsModule, only: GwfNpfOptionsType use BaseDisModule, only: DisBaseType use GwfIcModule, only: GwfIcType + use GwfVscModule, only: GwfVscType use Xt3dModule, only: Xt3dType use BlockParserModule, only: BlockParserType use InputOutputModule, only: GetUnit, openfile @@ -33,6 +34,7 @@ module GwfNpfModule type, extends(NumericalPackageType) :: GwfNpfType type(GwfIcType), pointer :: ic => null() !< initial conditions object + type(GwfVscType), pointer :: vsc => null() !< viscosity object type(Xt3dType), pointer :: xt3d => null() !< xt3d pointer integer(I4B), pointer :: iname => null() !< length of variable names character(len=24), dimension(:), pointer :: aname => null() !< variable names @@ -65,6 +67,9 @@ module GwfNpfModule real(DP), dimension(:), pointer, contiguous :: k11 => null() !< hydraulic conductivity; if anisotropic, then this is Kx prior to rotation real(DP), dimension(:), pointer, contiguous :: k22 => null() !< hydraulic conductivity; if specified then this is Ky prior to rotation real(DP), dimension(:), pointer, contiguous :: k33 => null() !< hydraulic conductivity; if specified then this is Kz prior to rotation + real(DP), dimension(:), pointer, contiguous :: k11input => null() !< hydraulic conductivity originally specified by user prior to TVK or VSC modification + real(DP), dimension(:), pointer, contiguous :: k22input => null() !< hydraulic conductivity originally specified by user prior to TVK or VSC modification + real(DP), dimension(:), pointer, contiguous :: k33input => null() !< hydraulic conductivity originally specified by user prior to TVK or VSC modification integer(I4B), pointer :: iavgkeff => null() !< effective conductivity averaging (0: harmonic, 1: arithmetic) integer(I4B), pointer :: ik22 => null() !< flag that k22 is specified integer(I4B), pointer :: ik33 => null() !< flag that k33 is specified @@ -92,7 +97,9 @@ module GwfNpfModule real(DP), dimension(:, :), pointer, contiguous :: propsedge => null() !< edge properties (Q, area, nx, ny, distance) ! integer(I4B), pointer :: intvk => null() ! TVK (time-varying K) unit number (0 if unused) + integer(I4B), pointer :: invsc => null() ! VSC (viscosity) unit number (0 if unused); viscosity leads to time-varying K's type(TvkType), pointer :: tvk => null() ! TVK object + !type(GwfVscType), pointer :: vsc => null() ! VSC object integer(I4B), pointer :: kchangeper => null() ! last stress period in which any node K (or K22, or K33) values were changed (0 if unchanged from start of simulation) integer(I4B), pointer :: kchangestp => null() ! last time step in which any node K (or K22, or K33) values were changed (0 if unchanged from start of simulation) integer(I4B), dimension(:), pointer, contiguous :: nodekchange => null() ! grid array of flags indicating for each node whether its K (or K22, or K33) value changed (1) at (kchangeper, kchangestp) or not (0) @@ -117,6 +124,7 @@ module GwfNpfModule procedure, private :: wd => sgwf_npf_wetdry procedure, private :: wdmsg => sgwf_npf_wdmsg procedure :: allocate_scalars + procedure, private :: store_original_k_arrays procedure, private :: allocate_arrays procedure, private :: source_options procedure, private :: source_griddata @@ -152,7 +160,7 @@ subroutine npf_cr(npfobj, name_model, inunit, iout) use IdmMf6FileLoaderModule, only: input_load use ConstantsModule, only: LENPACKAGETYPE ! -- dummy - type(GwfNpftype), pointer :: npfobj + type(GwfNpfType), pointer :: npfobj character(len=*), intent(in) :: name_model integer(I4B), intent(in) :: inunit integer(I4B), intent(in) :: iout @@ -201,7 +209,7 @@ end subroutine npf_cr !! should be passed. A consistency check is performed, and finally !! xt3d_df is called, when enabled. !< - subroutine npf_df(this, dis, xt3d, ingnc, npf_options) + subroutine npf_df(this, dis, xt3d, ingnc, invsc, npf_options) ! ****************************************************************************** ! npf_df -- Define ! ****************************************************************************** @@ -216,6 +224,7 @@ subroutine npf_df(this, dis, xt3d, ingnc, npf_options) class(DisBaseType), pointer, intent(inout) :: dis !< the pointer to the discretization type(Xt3dType), pointer :: xt3d !< the pointer to the XT3D 'package' integer(I4B), intent(in) :: ingnc !< ghostnodes enabled? (>0 means yes) + integer(I4B), intent(in) :: invsc !< viscosity enabled? (>0 means yes) type(GwfNpfOptionsType), optional, intent(in) :: npf_options !< the optional options, for when not constructing from file ! -- local ! -- data @@ -224,6 +233,9 @@ subroutine npf_df(this, dis, xt3d, ingnc, npf_options) ! -- Set a pointer to dis this%dis => dis ! + ! -- Set flag signifying whether vsc is active + if (invsc > 0) this%invsc = invsc + ! if (.not. present(npf_options)) then ! ! -- source options @@ -310,7 +322,7 @@ end subroutine npf_mc !! Allocate remaining package arrays, preprocess the input data and !! call *_ar on xt3d, when active. !< - subroutine npf_ar(this, ic, ibound, hnew) + subroutine npf_ar(this, ic, vsc, ibound, hnew) ! ****************************************************************************** ! npf_ar -- Allocate and Read ! ****************************************************************************** @@ -322,6 +334,7 @@ subroutine npf_ar(this, ic, ibound, hnew) ! -- dummy class(GwfNpftype) :: this !< instance of the NPF package type(GwfIcType), pointer, intent(in) :: ic !< initial conditions + type(GwfVscType), pointer, intent(in) :: vsc !< viscosity package integer(I4B), dimension(:), pointer, contiguous, intent(inout) :: ibound !< model ibound array real(DP), dimension(:), pointer, contiguous, intent(inout) :: hnew !< pointer to model head array ! -- local @@ -346,6 +359,21 @@ subroutine npf_ar(this, ic, ibound, hnew) end do end if ! + ! -- Store pointer to VSC if active + if (this%invsc /= 0) then + this%vsc => vsc + end if + + ! + ! -- allocate arrays to store original user input in case TVK/VSC modify them + if (this%invsc > 0) then + ! Need to allocate arrays that will store the original K values so + ! that the current K11 etc. carry the "real" K's that are updated by the + ! vscratio. This approach leverages existing functionality that makes use + ! of K. + call this%store_original_k_arrays(this%dis%nodes, this%dis%njas) + end if + ! ! -- preprocess data call this%preprocess_input() ! @@ -425,6 +453,12 @@ subroutine npf_ad(this, nodes, hold, hnew, irestore) call this%tvk%ad() end if ! + ! -- VSC + ! -- Hit the TVK-updated K's with VSC correction before calling/updating condsat + if (this%invsc /= 0) then + call this%vsc%update_k_with_vsc() + end if + ! ! -- If any K values have changed, we need to update CONDSAT or XT3D arrays if (this%kchangeper == kper .and. this%kchangestp == kstp) then if (this%ixt3d == 0) then @@ -1100,6 +1134,7 @@ subroutine npf_da(this) call mem_deallocate(this%ik22overk) call mem_deallocate(this%ik33overk) call mem_deallocate(this%intvk) + call mem_deallocate(this%invsc) call mem_deallocate(this%kchangeper) call mem_deallocate(this%kchangestp) ! @@ -1110,6 +1145,9 @@ subroutine npf_da(this) call mem_deallocate(this%k11) call mem_deallocate(this%k22) call mem_deallocate(this%k33) + call mem_deallocate(this%k11input) + call mem_deallocate(this%k22input) + call mem_deallocate(this%k33input) call mem_deallocate(this%sat) call mem_deallocate(this%condsat) call mem_deallocate(this%wetdry) @@ -1179,6 +1217,7 @@ subroutine allocate_scalars(this) call mem_allocate(this%nedges, 'NEDGES', this%memoryPath) call mem_allocate(this%lastedge, 'LASTEDGE', this%memoryPath) call mem_allocate(this%intvk, 'INTVK', this%memoryPath) + call mem_allocate(this%invsc, 'INVSC', this%memoryPath) call mem_allocate(this%kchangeper, 'KCHANGEPER', this%memoryPath) call mem_allocate(this%kchangestp, 'KCHANGESTP', this%memoryPath) ! @@ -1220,6 +1259,7 @@ subroutine allocate_scalars(this) this%nedges = 0 this%lastedge = 0 this%intvk = 0 + this%invsc = 0 this%kchangeper = 0 this%kchangestp = 0 ! @@ -1230,6 +1270,35 @@ subroutine allocate_scalars(this) return end subroutine allocate_scalars + subroutine store_original_k_arrays(this, ncells, njas) +! +! SPECIFICATIONS: +! ------------------------------------------------------------------------------ + ! -- modules + use MemoryManagerModule, only: mem_allocate + ! -- dummy + class(GwfNpftype) :: this + integer(I4B), intent(in) :: ncells + integer(I4B), intent(in) :: njas + ! -- local + integer(I4B) :: n +! ------------------------------------------------------------------------------ + ! + ! -- Retain copy of user-specified K arrays + do n = 1, ncells + this%k11input(n) = this%k11(n) + if (this%ik22 /= 0) then + this%k22input(n) = this%k22(n) + end if + if (this%ik33 /= 0) then + this%k33input(n) = this%k33(n) + end if + end do + ! + ! -- Return + return + end subroutine store_original_k_arrays + subroutine allocate_arrays(this, ncells, njas) ! ****************************************************************************** ! allocate_arrays -- Allocate npf arrays @@ -1256,6 +1325,9 @@ subroutine allocate_arrays(this, ncells, njas) ! -- Optional arrays dimensioned to full size initially call mem_allocate(this%k22, ncells, 'K22', this%memoryPath) call mem_allocate(this%k33, ncells, 'K33', this%memoryPath) + call mem_allocate(this%k11input, ncells, 'K11INPUT', this%memoryPath) + call mem_allocate(this%k22input, ncells, 'K22INPUT', this%memoryPath) + call mem_allocate(this%k33input, ncells, 'K33INPUT', this%memoryPath) call mem_allocate(this%wetdry, ncells, 'WETDRY', this%memoryPath) call mem_allocate(this%angle1, ncells, 'ANGLE1', this%memoryPath) call mem_allocate(this%angle2, ncells, 'ANGLE2', this%memoryPath) diff --git a/src/Model/GroundWaterFlow/gwf3riv8.f90 b/src/Model/GroundWaterFlow/gwf3riv8.f90 index f14e93fa2f7..31357fb05b4 100644 --- a/src/Model/GroundWaterFlow/gwf3riv8.f90 +++ b/src/Model/GroundWaterFlow/gwf3riv8.f90 @@ -1,7 +1,8 @@ module rivmodule - use KindModule, only: DP, I4B - use ConstantsModule, only: DZERO, LENFTYPE, LENPACKAGENAME - use MemoryHelperModule, only: create_mem_path + use KindModule, only: DP, I4B, LGP + use ConstantsModule, only: DZERO, LENFTYPE, LENPACKAGENAME, LENMEMPATH, & + LENVARNAME, LENMEMSEPARATOR + use MemoryHelperModule, only: create_mem_path, split_mem_address use BndModule, only: BndType use ObsModule, only: DefaultObsIdProcessor use TimeSeriesLinkModule, only: TimeSeriesLinkType, & @@ -15,6 +16,7 @@ module rivmodule ! character(len=LENFTYPE) :: ftype = 'RIV' character(len=LENPACKAGENAME) :: text = ' RIV' + character(len=LENMEMSEPARATOR), parameter :: memPathSeparator = '/' ! type, extends(BndType) :: RivType contains @@ -51,6 +53,10 @@ subroutine riv_create(packobj, id, ibcnum, inunit, iout, namemodel, pakname) character(len=*), intent(in) :: pakname ! -- local type(RivType), pointer :: rivobj + character(len=LENMEMPATH) :: vscpath !< if vsc exist, this is path name + character(len=LENMEMPATH) :: locmempath !< the memory path for the model + character(len=LENVARNAME) :: locvarname !< the package name to check on + logical(LGP) :: vscexists !< flag will be true if vsc is active ! ------------------------------------------------------------------------------ ! ! -- allocate the object and assign values to object variables @@ -75,6 +81,13 @@ subroutine riv_create(packobj, id, ibcnum, inunit, iout, namemodel, pakname) packobj%iscloc = 2 !sfac applies to conductance packobj%ictMemPath = create_mem_path(namemodel, 'NPF') ! + ! -- check if vsc package exists and set flag if so + vscpath = trim(namemodel)//memPathSeparator//'VSC' + call split_mem_address(vscpath, locmempath, locvarname, vscexists) + if (vscexists) then + packobj%ivsc = 1 + end if + ! ! -- return return end subroutine riv_create diff --git a/src/Model/GroundWaterFlow/gwf3sfr8.f90 b/src/Model/GroundWaterFlow/gwf3sfr8.f90 index 8f9a6ae28b9..a8dd1ddd950 100644 --- a/src/Model/GroundWaterFlow/gwf3sfr8.f90 +++ b/src/Model/GroundWaterFlow/gwf3sfr8.f90 @@ -146,6 +146,9 @@ module SfrModule integer(I4B), pointer :: idense !< flag indicating if density corrections are active real(DP), dimension(:, :), pointer, contiguous :: denseterms => null() !< density terms ! + ! -- viscosity variables + real(DP), dimension(:, :), pointer, contiguous :: viscratios => null() !< viscosity ratios (1: sfr vsc ratio; 2: gwf vsc ratio) + ! ! -- type bound procedures contains procedure :: sfr_allocate_scalars @@ -208,6 +211,8 @@ module SfrModule ! -- density procedure :: sfr_activate_density procedure, private :: sfr_calculate_density_exchange + ! -- viscosity + procedure :: sfr_activate_viscosity end type SfrType contains @@ -316,6 +321,7 @@ subroutine sfr_allocate_scalars(this) this%icheck = 1 this%iconvchk = 1 this%idense = 0 + this%ivsc = 0 this%ianynone = 0 this%ncrossptstot = 0 ! @@ -499,12 +505,15 @@ subroutine sfr_allocate_arrays(this) this%qauxcbc(i) = DZERO end do ! - !-- fill cauxcbc + ! -- fill cauxcbc this%cauxcbc(1) = 'FLOW-AREA ' ! ! -- allocate denseterms to size 0 call mem_allocate(this%denseterms, 3, 0, 'DENSETERMS', this%memoryPath) ! + ! -- allocate viscratios to size 0 + call mem_allocate(this%viscratios, 2, 0, 'VISCRATIOS', this%memoryPath) + ! ! -- return return end subroutine sfr_allocate_arrays @@ -2471,7 +2480,7 @@ subroutine sfr_ot_dv(this, idvsave, idvprint) call this%stagetab%add_term(stage) call this%stagetab%add_term(depth) call this%stagetab%add_term(w) - call this%sfr_calc_cond(n, depth, cond) + call this%sfr_calc_cond(n, depth, cond, stage, hgwf) if (node > 0) then sbot = this%strtop(n) - this%bthick(n) if (hgwf < sbot) then @@ -2557,6 +2566,7 @@ subroutine sfr_da(this) call mem_deallocate(this%stage0) call mem_deallocate(this%usflow0) call mem_deallocate(this%denseterms) + call mem_deallocate(this%viscratios) ! ! -- deallocate reach order and connection data call mem_deallocate(this%isfrorder) @@ -3405,7 +3415,7 @@ subroutine sfr_solve(this, n, h, hcof, rhs, update) ! ! -- calculate reach conductance for a unit depth of water ! if equal to zero will skip iterations - call this%sfr_calc_cond(n, d1, cstr) + call this%sfr_calc_cond(n, d1, cstr, hsfr, hgwf) ! ! -- set flag to skip iterations isolve = 1 @@ -3968,10 +3978,7 @@ subroutine sfr_calc_qgwf(this, n, depth, hgwf, qgwf, gwfhcof, gwfrhs) ! -- calculate saturation call sChSmooth(depth, sat, derv) ! - ! -- calculate conductance - call this%sfr_calc_cond(n, depth, cond) - ! - ! -- calculate groundwater leakage + ! -- terms for calculating direction of gradient across streambed tp = this%strtop(n) bt = tp - this%bthick(n) hsfr = tp + depth @@ -3979,6 +3986,11 @@ subroutine sfr_calc_qgwf(this, n, depth, hgwf, qgwf, gwfhcof, gwfrhs) if (htmp < bt) then htmp = bt end if + ! + ! -- calculate conductance + call this%sfr_calc_cond(n, depth, cond, hsfr, htmp) + ! + ! -- calculate groundwater leakage qgwf = sat * cond * (htmp - hsfr) gwfrhs0 = -sat * cond * hsfr gwfhcof0 = -sat * cond @@ -4002,25 +4014,41 @@ end subroutine sfr_calc_qgwf !! Method to calculate the reach-aquifer conductance for a SFR package reach. !! !< - subroutine sfr_calc_cond(this, n, depth, cond) + subroutine sfr_calc_cond(this, n, depth, cond, hsfr, htmp) ! -- dummy variables class(SfrType) :: this !< SfrType object integer(I4B), intent(in) :: n !< reach number real(DP), intent(in) :: depth !< reach depth real(DP), intent(inout) :: cond !< reach-aquifer conductance + real(DP), intent(in), optional :: hsfr !< stream stage + real(DP), intent(in), optional :: htmp !< head in gw cell ! -- local variables integer(I4B) :: node real(DP) :: wp + real(DP) :: vscratio ! ! -- initialize conductance cond = DZERO ! + ! -- initial viscosity ratio to 1 + vscratio = DONE + ! ! -- calculate conductance if GWF cell is active node = this%igwfnode(n) if (node > 0) then if (this%ibound(node) > 0) then + ! + ! -- direction of gradient across streambed determines which vsc ratio + if (this%ivsc == 1) then + if (hsfr > htmp) then + ! strm stg > gw head + vscratio = this%viscratios(1, n) + else if (htmp > hsfr) then + vscratio = this%viscratios(2, n) + end if + end if wp = this%calc_perimeter_wet(n, depth) - cond = this%hk(n) * this%length(n) * wp / this%bthick(n) + cond = this%hk(n) * vscratio * this%length(n) * wp / this%bthick(n) end if end if ! @@ -5563,6 +5591,36 @@ subroutine sfr_activate_density(this) return end subroutine sfr_activate_density + !> @brief Activate viscosity terms + !! + !! Method to activate addition of viscosity terms for a SFR package reach. + !! + !< + subroutine sfr_activate_viscosity(this) + ! -- modules + use MemoryManagerModule, only: mem_reallocate + ! -- dummy variables + class(SfrType), intent(inout) :: this !< SfrType object + ! -- local variables + integer(I4B) :: i + integer(I4B) :: j + ! + ! -- Set ivsc and reallocate viscratios to be of size MAXBOUND + this%ivsc = 1 + call mem_reallocate(this%viscratios, 2, this%MAXBOUND, 'VISCRATIOS', & + this%memoryPath) + do i = 1, this%maxbound + do j = 1, 2 + this%viscratios(j, i) = DZERO + end do + end do + write (this%iout, '(/1x,a)') 'VISCOSITY HAS BEEN ACTIVATED FOR SFR & + &PACKAGE: '//trim(adjustl(this%packName)) + ! + ! -- return + return + end subroutine sfr_activate_viscosity + !> @brief Calculate density terms !! !! Method to galculate groundwater-reach density exchange terms for a diff --git a/src/Model/GroundWaterFlow/gwf3vsc8.f90 b/src/Model/GroundWaterFlow/gwf3vsc8.f90 new file mode 100644 index 00000000000..d3846731441 --- /dev/null +++ b/src/Model/GroundWaterFlow/gwf3vsc8.f90 @@ -0,0 +1,1494 @@ +! Viscosity Package for representing variable-viscosity groundwater flow + +module GwfVscModule + + use KindModule, only: DP, I4B + use SimModule, only: store_error, store_warning, count_errors + use MemoryManagerModule, only: mem_allocate, mem_reallocate, & + mem_deallocate, mem_setptr + use MemoryHelperModule, only: create_mem_path + use ConstantsModule, only: DHALF, DZERO, DONE, LENMODELNAME, LENAUXNAME, & + DHNOFLO, MAXCHARLEN, LINELENGTH, LENMEMPATH + use TdisModule, only: kper, kstp + use NumericalPackageModule, only: NumericalPackageType + use BaseDisModule, only: DisBaseType + use GwfVscInputDataModule, only: GwfVscInputDataType + use ListsModule, only: basemodellist + + implicit none + + private + public :: GwfVscType + public :: vsc_cr + + type :: ConcentrationPointer + real(DP), dimension(:), pointer :: conc => null() !< pointer to concentration array + integer(I4B), dimension(:), pointer :: icbund => null() !< store pointer to gwt ibound array + end type ConcentrationPointer + + type, extends(NumericalPackageType) :: GwfVscType + integer(I4B), pointer :: thermivisc => null() !< viscosity formulation flag (1:Linear, 2:Nonlinear) + integer(I4B), pointer :: idxtmpr => null() !< if greater than 0 then an index for identifying whether the "species" array is temperature + integer(I4B), pointer :: ioutvisc => null() !< unit number for saving viscosity + integer(I4B), pointer :: iconcset => null() !< if 1 then conc points to a gwt (or gwe) model%x array + integer(I4B), pointer :: ireadelev => null() !< if 1 then elev has been allocated and filled + integer(I4B), dimension(:), pointer :: ivisc => null() !< viscosity formulation flag for each species (1:Linear, 2:Nonlinear) + real(DP), pointer :: viscref => null() !< reference fluid viscosity + real(DP), dimension(:), pointer, contiguous :: visc => null() !< viscosity + real(DP), dimension(:), pointer, contiguous :: concvsc => null() !< concentration (or temperature) array if specified in vsc package ! kluge note: is this ever really used? + real(DP), dimension(:), pointer, contiguous :: elev => null() !< cell center elevation (optional; if not specified, then use (top+bot)/2) + integer(I4B), dimension(:), pointer :: ibound => null() !< store pointer to ibound + + integer(I4B), pointer :: nviscspecies => null() !< number of concentration species used in viscosity equation + real(DP), dimension(:), pointer, contiguous :: dviscdc => null() !< change in viscosity with change in concentration ! kluge note: parameters will depend on formula; linear for now + real(DP), dimension(:), pointer, contiguous :: cviscref => null() !< reference concentration used in viscosity equation + real(DP), dimension(:), pointer, contiguous :: ctemp => null() !< temporary array of size (nviscspec) to pass to calc_visc_x + character(len=LENMODELNAME), dimension(:), allocatable :: cmodelname !< names of gwt (or gwe) models used in viscosity equation + character(len=LENAUXNAME), dimension(:), allocatable :: cauxspeciesname !< names of aux columns used in viscosity equation + ! + ! -- Viscosity constants + real(DP), pointer :: a2 => null() !< an empirical parameter specified by the user for calculating viscosity + real(DP), pointer :: a3 => null() !< an empirical parameter specified by the user for calculating viscosity + real(DP), pointer :: a4 => null() !< an empirical parameter specified by the user for calculating viscosity + + type(ConcentrationPointer), allocatable, dimension(:) :: modelconc !< concentration (or temperature) pointer for each solute (or heat) transport model + + integer(I4B), pointer :: ik22overk => null() !< NPF flag that k22 is specified as anisotropy ratio + integer(I4B), pointer :: ik33overk => null() !< NPF flag that k33 is specified as anisotropy ratio + real(DP), dimension(:), pointer, contiguous :: k11 => null() !< NPF hydraulic conductivity; if anisotropic, then this is Kx prior to rotation + real(DP), dimension(:), pointer, contiguous :: k22 => null() !< NPF hydraulic conductivity; if specified then this is Ky prior to rotation + real(DP), dimension(:), pointer, contiguous :: k33 => null() !< NPF hydraulic conductivity; if specified then this is Kz prior to rotation + real(DP), dimension(:), pointer, contiguous :: k11input => null() !< NPF hydraulic conductivity as originally specified by the user + real(DP), dimension(:), pointer, contiguous :: k22input => null() !< NPF hydraulic conductivity as originally specified by the user + real(DP), dimension(:), pointer, contiguous :: k33input => null() !< NPF hydraulic conductivity as originally specified by the user + integer(I4B), pointer :: ik22 => null() !< NPF flag that k22 is specified + integer(I4B), pointer :: ik33 => null() !< NPF flag that k33 is specified + integer(I4B), pointer :: kchangeper => null() ! last stress period in which any node K (or K22, or K33) values were changed (0 if unchanged from start of simulation) + integer(I4B), pointer :: kchangestp => null() ! last time step in which any node K (or K22, or K33) values were changed (0 if unchanged from start of simulation) + integer(I4B), dimension(:), pointer, contiguous :: nodekchange => null() ! grid array of flags indicating for each node whether its K (or K22, or K33) value changed (1) at (kchangeper, kchangestp) or not (0) + + contains + procedure :: vsc_df + procedure :: vsc_ar + procedure, public :: vsc_ar_bnd + procedure :: vsc_rp + procedure :: vsc_ad + procedure, public :: vsc_ad_bnd + procedure :: vsc_ot_dv + procedure :: vsc_da + procedure, private :: vsc_calcvisc + procedure :: allocate_scalars + procedure, private :: allocate_arrays + procedure, private :: read_options + procedure, private :: set_options + procedure, private :: read_dimensions + procedure, private :: read_packagedata + procedure, private :: set_packagedata + procedure, private :: set_npf_pointers + procedure, public :: update_k_with_vsc + procedure, private :: vsc_set_changed_at + procedure :: set_concentration_pointer + end type GwfVscType + +contains + + function calc_visc(ivisc, viscref, dviscdc, cviscref, conc, & + a2, a3, a4) result(visc) +! ****************************************************************************** +! calc_visc -- generic function to calculate changes in fluid viscosity +! using a linear formulation +! ****************************************************************************** +! +! SPECIFICATIONS: +! ------------------------------------------------------------------------------ + + ! -- dummy + integer(I4B), dimension(:), intent(in) :: ivisc + real(DP), intent(in) :: viscref + real(DP), dimension(:), intent(in) :: dviscdc + real(DP), dimension(:), intent(in) :: cviscref + real(DP), dimension(:), intent(in) :: conc + real(DP), intent(in) :: a2, a3, a4 + ! -- return + real(DP) :: visc + ! -- local + integer(I4B) :: nviscspec + integer(I4B) :: i + real(DP) :: mu_t + real(DP) :: expon +! ------------------------------------------------------------------------------ + ! + nviscspec = size(dviscdc) + visc = viscref + + do i = 1, nviscspec + if (ivisc(i) == 1) then + visc = visc + dviscdc(i) * (conc(i) - cviscref(i)) ! kluge note: linear for now + else + expon = -1 * a3 * ((conc(i) - cviscref(i)) / & + ((conc(i) + a4) * (cviscref(i) + a4))) + mu_t = viscref * a2**expon + ! If a nonlinear correction is applied, then b/c it takes into account + ! viscref, need to subtract it in this case + ! At most, there will only ever be 1 nonlinear correction + visc = (visc - viscref) + mu_t + end if + ! end if + end do + + ! NOTES (from in-person meeting with Alden) + ! Order matters!! (This assumes we apply the temperature correction after + ! accounting for solute concentrations) + ! REMEMBER: idxtmpr + ! For the case i == idxtmpr + ! special multiplicative eqn here that leverages idxtmpr + ! - check to make sure that idxtmpr is not zero b/c that means there + ! is no temperature (remember to initialize idxtmpr to 0) + + ! + ! -- return + return + end function calc_visc + + subroutine vsc_cr(vscobj, name_model, inunit, iout) +! ****************************************************************************** +! vsc_cr -- Create a new VSC object +! ****************************************************************************** +! +! SPECIFICATIONS: +! ------------------------------------------------------------------------------ + ! -- dummy + type(GwfVscType), pointer :: vscobj + character(len=*), intent(in) :: name_model + integer(I4B), intent(in) :: inunit + integer(I4B), intent(in) :: iout +! ------------------------------------------------------------------------------ + ! + ! -- Create the object + allocate (vscobj) + ! + ! -- create name and memory path + call vscobj%set_names(1, name_model, 'VSC', 'VSC') + ! + ! -- Allocate scalars + call vscobj%allocate_scalars() + ! + ! -- Set variables + vscobj%inunit = inunit + vscobj%iout = iout + ! + ! -- Initialize block parser + call vscobj%parser%Initialize(vscobj%inunit, vscobj%iout) + ! + ! -- Return + return + end subroutine vsc_cr + + !> @brief Read options and package data, or set from argument + !< + subroutine vsc_df(this, dis, vsc_input) +! ****************************************************************************** +! vsc_df -- Define +! ****************************************************************************** +! +! SPECIFICATIONS: +! ------------------------------------------------------------------------------ + ! -- modules + ! -- dummy + class(GwfVscType) :: this !< this viscosity package + class(DisBaseType), pointer, intent(in) :: dis !< pointer to discretization + type(GwfVscInputDataType), optional, intent(in) :: vsc_input !< optional vsc input data, otherwise read from file + ! -- local + ! -- formats + character(len=*), parameter :: fmtvsc = & + "(1x,/1x,'VSC -- VISCOSITY PACKAGE, VERSION 1, 9/30/2023', & + &' INPUT READ FROM UNIT ', i0, //)" +! ------------------------------------------------------------------------------ + ! + ! --print a message identifying the viscosity package + write (this%iout, fmtvsc) this%inunit + ! + ! -- store pointers to arguments that were passed in + this%dis => dis + + if (.not. present(vsc_input)) then + ! + ! -- Read viscosity options + call this%read_options() + ! + ! -- Read viscosity dimensions + call this%read_dimensions() + else + ! set from input data instead + call this%set_options(vsc_input) + this%nviscspecies = vsc_input%nviscspecies + end if + ! + ! -- Allocate arrays + call this%allocate_arrays(dis%nodes) + + if (.not. present(vsc_input)) then + ! + ! -- Read viscosity packagedata + call this%read_packagedata() + else + ! set from input data instead + call this%set_packagedata(vsc_input) + end if + ! + ! -- Return + return + end subroutine vsc_df + + subroutine vsc_ar(this, ibound) +! ****************************************************************************** +! vsc_ar -- Allocate and Read +! ****************************************************************************** +! +! SPECIFICATIONS: +! ------------------------------------------------------------------------------ + ! -- modules + ! -- dummy + class(GwfVscType) :: this + integer(I4B), dimension(:), pointer :: ibound + ! -- local + ! -- formats +! ------------------------------------------------------------------------------ + ! + ! -- store pointers to arguments that were passed in + this%ibound => ibound + ! + ! -- Set pointers to npf variables + call this%set_npf_pointers() + ! + ! -- Return + return + end subroutine vsc_ar + + !> @brief Activate viscosity in advanced packages + !! + !! Viscosity ar_bnd rountine to activate viscosity in the advanced + !! packages. This routine is called from gwf_ar() as it moves through each + !! package + !! + !< + subroutine vsc_ar_bnd(this, packobj) + ! + ! SPECIFICATIONS: + ! ---------------------------------------------------------------------------- + ! -- modules + use BndModule, only: BndType + use LakModule, only: LakType + use SfrModule, only: SfrType + use MawModule, only: MawType + ! -- dummy + class(GwfVscType) :: this + class(BndType), pointer :: packobj + ! -- local + ! ---------------------------------------------------------------------------- + ! + ! -- Add density terms based on boundary package type + select case (packobj%filtyp) + case ('LAK') + ! + ! -- activate viscosity for lake package + select type (packobj) + type is (LakType) + call packobj%lak_activate_viscosity() + end select + + case ('SFR') + ! + ! -- activate viscosity for sfr package + select type (packobj) + type is (SfrType) + call packobj%sfr_activate_viscosity() + end select + + case ('MAW') + ! + ! -- activate viscosity for maw package + select type (packobj) + type is (MawType) + call packobj%maw_activate_viscosity() + end select + + case default + ! + ! -- nothing + end select + ! + ! -- Return + return + end subroutine vsc_ar_bnd + + !> @brief Set pointers to NPF variables + !! + !! Set array and variable pointers from the NPF + !! package for access by VSC. + !! + !< + subroutine set_npf_pointers(this) + ! -- dummy variables + class(GwfVscType) :: this + ! -- local variables + character(len=LENMEMPATH) :: npfMemoryPath + ! + ! -- Set pointers to other package variables + ! -- NPF + npfMemoryPath = create_mem_path(this%name_model, 'NPF') + call mem_setptr(this%ik22overk, 'IK22OVERK', npfMemoryPath) + call mem_setptr(this%ik33overk, 'IK33OVERK', npfMemoryPath) + call mem_setptr(this%k11, 'K11', npfMemoryPath) + call mem_setptr(this%k22, 'K22', npfMemoryPath) + call mem_setptr(this%k33, 'K33', npfMemoryPath) + call mem_setptr(this%k11input, 'K11INPUT', npfMemoryPath) + call mem_setptr(this%k22input, 'K22INPUT', npfMemoryPath) + call mem_setptr(this%k33input, 'K33INPUT', npfMemoryPath) + call mem_setptr(this%ik22, 'IK22', npfMemoryPath) + call mem_setptr(this%ik33, 'IK33', npfMemoryPath) + call mem_setptr(this%kchangeper, 'KCHANGEPER', npfMemoryPath) + call mem_setptr(this%kchangestp, 'KCHANGESTP', npfMemoryPath) + call mem_setptr(this%nodekchange, 'NODEKCHANGE', npfMemoryPath) + ! + return + end subroutine set_npf_pointers + + subroutine vsc_rp(this) +! ****************************************************************************** +! vsc_rp -- Check for new vsc period data +! ****************************************************************************** +! +! SPECIFICATIONS: +! ------------------------------------------------------------------------------ + ! -- modules + use TdisModule, only: kstp, kper + ! -- dummy + class(GwfVscType) :: this + ! -- local + character(len=LINELENGTH) :: errmsg + integer(I4B) :: i + ! -- formats + character(len=*), parameter :: fmtc = & + "('VISCOSITY PACKAGE DOES NOT HAVE A CONCENTRATION SET & + &FOR SPECIES ',i0,'. ONE OR MORE MODEL NAMES MAY BE SPECIFIED & + &INCORRECTLY IN THE PACKAGEDATA BLOCK OR A GWF-GWT EXCHANGE MAY NEED & + &TO BE ACTIVATED.')" +! ------------------------------------------------------------------------------ + ! + ! -- Check to make sure all concentration pointers have been set + if (kstp * kper == 1) then + do i = 1, this%nviscspecies + if (.not. associated(this%modelconc(i)%conc)) then + write (errmsg, fmtc) i + call store_error(errmsg) + end if + end do + if (count_errors() > 0) then + call this%parser%StoreErrorUnit() + end if + end if + ! + ! -- return + return + end subroutine vsc_rp + + subroutine vsc_ad(this) +! ****************************************************************************** +! vsc_ad -- Advance +! ****************************************************************************** +! +! SPECIFICATIONS: +! ------------------------------------------------------------------------------ + ! -- dummy + class(GwfVscType) :: this + ! -- local +! ------------------------------------------------------------------------------ + ! + ! -- update viscosity using the latest concentration/temperature + call this%vsc_calcvisc() + ! + ! -- Return + return + end subroutine vsc_ad + + !> @brief advance the boundary packages when viscosity is active + !! + !! Update the conductance values associate with inflow from a boundary + !! when VSC package is active. + !< + subroutine vsc_ad_bnd(this, packobj, hnew) + ! -- modules + use BndModule, only: BndType + ! -- dummy + class(GwfVscType) :: this + class(BndType), pointer :: packobj + real(DP), intent(in), dimension(:) :: hnew + ! -- local + integer(I4B) :: i, j + integer(I4B) :: n, locvisc, locelev + integer(I4B), dimension(:), allocatable :: locconc + ! + ! -- initialize + locvisc = 0 + locelev = 0 + allocate (locconc(this%nviscspecies)) + locconc(:) = 0 + ! + ! -- Add viscosity terms for conductance-dependent boundaries + do n = 1, packobj%naux + if (packobj%auxname(n) == 'VISCOSITY') then + locvisc = n + else if (packobj%auxname(n) == 'ELEVATION') then + locelev = n + end if + end do + ! + ! -- find aux columns for conc (or temp.) that affect viscosity + do i = 1, this%nviscspecies + locconc(i) = 0 + do j = 1, packobj%naux + if (this%cauxspeciesname(i) == packobj%auxname(j)) then + locconc(i) = j + exit + end if + end do + if (locconc(i) == 0) then + ! -- one not found, so don't use and mark all as 0 + locconc(:) = 0 + exit + end if + end do + ! + ! -- apply viscosity terms to inflow from boundary based on package type + select case (packobj%filtyp) + case ('GHB', 'DRN', 'RIV') + ! + ! -- general head, drain, and river boundary + call vsc_ad_standard_bnd(packobj, hnew, this%visc, this%viscref, & + locelev, locvisc, locconc, this%dviscdc, & + this%cviscref, this%ivisc, this%a2, this%a3, & + this%a4, this%ctemp) + case ('LAK') + ! + ! -- lake + ! Update 'viscratios' internal to lak such that they are + ! automatically applied in the LAK calc_cond() routine + call vsc_ad_lak(packobj, this%visc, this%viscref, this%elev, locvisc, & + locconc, this%dviscdc, this%cviscref, this%ivisc, & + this%a2, this%a3, this%a4, this%ctemp) + case ('SFR') + ! + ! -- streamflow routing + ! Update 'viscratios' internal to sfr such that they are + ! automatically applied in the SFR calc_cond() routine + call vsc_ad_sfr(packobj, this%visc, this%viscref, this%elev, locvisc, & + locconc, this%dviscdc, this%cviscref, this%ivisc, & + this%a2, this%a3, this%a4, this%ctemp) + case ('MAW') + ! + ! -- multi-aquifer well + call vsc_ad_maw(packobj, this%visc, this%viscref, this%elev, locvisc, & + locconc, this%dviscdc, this%cviscref, this%ivisc, & + this%a2, this%a3, this%a4, this%ctemp) + case ('UZF') + ! + ! -- unsaturated-zone flow + case default + ! + ! -- nothing + end select + ! + ! -- deallocate + deallocate (locconc) + ! + ! -- Return + return + end subroutine vsc_ad_bnd + + !> @brief advance ghb while accounting for viscosity + !! + !! When flow enters from ghb boundary type, take into account the effects + !! of viscosity on the user-specified conductance terms + !< + subroutine vsc_ad_standard_bnd(packobj, hnew, visc, viscref, locelev, & + locvisc, locconc, dviscdc, cviscref, & + ivisc, a2, a3, a4, ctemp) + ! -- modules + use BndModule, only: BndType + class(BndType), pointer :: packobj + ! -- dummy + real(DP), intent(in), dimension(:) :: hnew + real(DP), intent(in), dimension(:) :: visc + real(DP), intent(in) :: a2, a3, a4 + real(DP), intent(in) :: viscref + integer(I4B), intent(in) :: locelev + integer(I4B), intent(in) :: locvisc + integer(I4B), dimension(:), intent(in) :: locconc + integer(I4B), dimension(:), intent(in) :: ivisc + real(DP), dimension(:), intent(in) :: dviscdc + real(DP), dimension(:), intent(in) :: cviscref + real(DP), dimension(:), intent(inout) :: ctemp + ! -- local + integer(I4B) :: n + integer(I4B) :: node + real(DP) :: viscghb + real(DP) :: viscratio +! ------------------------------------------------------------------------------- + ! + ! -- Process density terms for each GHB + do n = 1, packobj%nbound + node = packobj%nodelist(n) + ! + ! -- Check if boundary cell is active, cycle if not + if (packobj%ibound(node) <= 0) cycle + ! + ! -- calculate the viscosity associcated with the boundary + viscghb = calc_bnd_viscosity(n, locvisc, locconc, viscref, dviscdc, & + cviscref, ctemp, ivisc, a2, a3, a4, & + packobj%auxvar) + ! + ! -- update boundary conductance based on viscosity effects + packobj%bound(2, n) = update_bnd_cond(viscghb, viscref, & + packobj%condinput(n)) + ! + end do + ! + ! -- Return + return + end subroutine vsc_ad_standard_bnd + + !> @brief Update sfr-related viscosity ratios + !! + !! When the viscosity package is active, update the viscosity ratio that is + !! applied to the hydraulic conductivity specified in the SFR package + !< + subroutine vsc_ad_sfr(packobj, visc, viscref, elev, locvisc, locconc, & + dviscdc, cviscref, ivisc, a2, a3, a4, ctemp) + ! -- modules + use BndModule, only: BndType + use SfrModule, only: SfrType + class(BndType), pointer :: packobj + ! -- dummy + real(DP), intent(in) :: viscref + real(DP), intent(in) :: a2, a3, a4 + integer(I4B), intent(in) :: locvisc + integer(I4B), dimension(:), intent(in) :: locconc + integer(I4B), dimension(:), intent(in) :: ivisc + real(DP), dimension(:), intent(in) :: visc + real(DP), dimension(:), intent(in) :: elev + real(DP), dimension(:), intent(in) :: dviscdc + real(DP), dimension(:), intent(in) :: cviscref + real(DP), dimension(:), intent(inout) :: ctemp + ! -- local + integer(I4B) :: n + integer(I4B) :: node + real(DP) :: viscsfr +! ------------------------------------------------------------------------------- + ! + ! -- update viscosity ratios for updating hyd. cond (and conductance) + select type (packobj) + type is (SfrType) + do n = 1, packobj%nbound + ! + ! -- get gwf node number + node = packobj%nodelist(n) + ! + ! -- Check if boundary cell is active, cycle if not + if (packobj%ibound(node) <= 0) cycle + ! + ! -- + ! + ! -- calculate the viscosity associcated with the boundary + viscsfr = calc_bnd_viscosity(n, locvisc, locconc, viscref, dviscdc, & + cviscref, ctemp, ivisc, a2, a3, a4, & + packobj%auxvar) + ! + ! -- fill sfr relative viscosity into column 1 of viscratios + packobj%viscratios(1, n) = calc_vsc_ratio(viscref, viscsfr) + ! + ! -- fill gwf relative viscosity into column 2 of viscratios + packobj%viscratios(2, n) = calc_vsc_ratio(viscref, visc(node)) + end do + end select + ! + ! -- Return + return + end subroutine vsc_ad_sfr + + !> @brief Update lak-related viscosity ratios + !! + !! When the viscosity package is active, update the viscosity ratio that is + !! applied to the lakebed conductance calculated in the LAK package + !< + subroutine vsc_ad_lak(packobj, visc, viscref, elev, locvisc, locconc, & + dviscdc, cviscref, ivisc, a2, a3, a4, ctemp) + ! -- modules + use BndModule, only: BndType + use LakModule, only: LakType + class(BndType), pointer :: packobj + ! -- dummy + real(DP), intent(in) :: viscref + real(DP), intent(in) :: a2, a3, a4 + integer(I4B), intent(in) :: locvisc + integer(I4B), dimension(:), intent(in) :: locconc + integer(I4B), dimension(:), intent(in) :: ivisc + real(DP), dimension(:), intent(in) :: visc + real(DP), dimension(:), intent(in) :: elev + real(DP), dimension(:), intent(in) :: dviscdc + real(DP), dimension(:), intent(in) :: cviscref + real(DP), dimension(:), intent(inout) :: ctemp + ! -- local + integer(I4B) :: n + integer(I4B) :: node + real(DP) :: visclak +! ------------------------------------------------------------------------------- + ! + ! -- update viscosity ratios for updating hyd. cond (and conductance) + select type (packobj) + type is (LakType) + do n = 1, packobj%nbound + ! + ! -- get gwf node number + node = packobj%nodelist(n) + ! + ! -- Check if boundary cell is active, cycle if not + if (packobj%ibound(node) <= 0) cycle + ! + ! -- + ! + ! -- calculate the viscosity associcated with the boundary + visclak = calc_bnd_viscosity(n, locvisc, locconc, viscref, dviscdc, & + cviscref, ctemp, ivisc, a2, a3, a4, & + packobj%auxvar) + ! + ! -- fill lak relative viscosity into column 1 of viscratios + packobj%viscratios(1, n) = calc_vsc_ratio(viscref, visclak) + ! + ! -- fill gwf relative viscosity into column 2 of viscratios + packobj%viscratios(2, n) = calc_vsc_ratio(viscref, visc(node)) + end do + end select + ! + ! -- Return + return + end subroutine vsc_ad_lak + + !> @brief Update maw-related viscosity ratios + !! + !! When the viscosity package is active, update the viscosity ratio that is + !! applied to the conductance calculated in the MAW package + !< + subroutine vsc_ad_maw(packobj, visc, viscref, elev, locvisc, locconc, & + dviscdc, cviscref, ivisc, a2, a3, a4, ctemp) + ! -- modules + use BndModule, only: BndType + use MawModule, only: MawType + class(BndType), pointer :: packobj + ! -- dummy + real(DP), intent(in) :: viscref + real(DP), intent(in) :: a2, a3, a4 + integer(I4B), intent(in) :: locvisc + integer(I4B), dimension(:), intent(in) :: locconc + integer(I4B), dimension(:), intent(in) :: ivisc + real(DP), dimension(:), intent(in) :: visc + real(DP), dimension(:), intent(in) :: elev + real(DP), dimension(:), intent(in) :: dviscdc + real(DP), dimension(:), intent(in) :: cviscref + real(DP), dimension(:), intent(inout) :: ctemp + ! -- local + integer(I4B) :: n + integer(I4B) :: node + real(DP) :: viscmaw +! ------------------------------------------------------------------------------- + ! + ! -- update viscosity ratios for updating hyd. cond (and conductance) + select type (packobj) + type is (MawType) + do n = 1, packobj%nbound + ! + ! -- get gwf node number + node = packobj%nodelist(n) + ! + ! -- Check if boundary cell is active, cycle if not + if (packobj%ibound(node) <= 0) cycle + ! + ! -- + ! + ! -- calculate the viscosity associcated with the boundary + viscmaw = calc_bnd_viscosity(n, locvisc, locconc, viscref, dviscdc, & + cviscref, ctemp, ivisc, a2, a3, a4, & + packobj%auxvar) + ! + ! -- fill lak relative viscosity into column 1 of viscratios + packobj%viscratios(1, n) = calc_vsc_ratio(viscref, viscmaw) + ! + ! -- fill gwf relative viscosity into column 2 of viscratios + packobj%viscratios(2, n) = calc_vsc_ratio(viscref, visc(node)) + end do + end select + ! + ! -- Return + return + end subroutine vsc_ad_maw + + !> @brief apply bnd viscosity to the conductance term + !! + !! When the viscosity package is active apply the viscosity ratio to the + !! active boundary package's conductance term. + !< + function update_bnd_cond(bndvisc, viscref, spcfdcond) result(updatedcond) + ! -- modules + ! -- dummy + real(DP), intent(in) :: viscref + real(DP), intent(in) :: bndvisc + real(DP), intent(in) :: spcfdcond + ! -- local + real(DP) :: vscratio + real(DP) :: updatedcond + integer(I4B) :: n +! ------------------------------------------------------------------------------- + ! + vscratio = calc_vsc_ratio(viscref, bndvisc) + ! + ! -- calculate new conductance here!! + updatedcond = vscratio * spcfdcond + ! + ! -- Return + return + end function update_bnd_cond + + !> @brief calculate and return the viscosity ratio + !< + function calc_vsc_ratio(viscref, bndvisc) result(viscratio) + ! -- dummy + real(DP), intent(in) :: viscref + real(DP), intent(in) :: bndvisc + ! -- local + real(DP) :: viscratio +! ------------------------------------------------------------------------------- + ! + viscratio = viscref / bndvisc + ! + ! -- Return + return + end function calc_vsc_ratio + + function calc_bnd_viscosity(n, locvisc, locconc, viscref, dviscdc, cviscref, & + ! ctemp, ivisc, auxvar) result(viscbnd) + ctemp, ivisc, a2, a3, a4, auxvar) result(viscbnd) +! ****************************************************************************** +! get_bnd_viscosity -- Return the viscosity of the boundary package using one of +! several different options in the following order of priority: +! 1. Assign as aux variable in column with name 'VISCOSITY' +! 2. Calculate using viscosity equation and nviscspecies aux columns +! 3. If neither of those, then assign as viscref +! ****************************************************************************** +! +! SPECIFICATIONS: +! ------------------------------------------------------------------------------ + ! -- modules + ! -- dummy + integer(I4B), intent(in) :: n + integer(I4B), intent(in) :: locvisc + real(DP), intent(in) :: a2, a3, a4 + integer(I4B), dimension(:), intent(in) :: ivisc + integer(I4B), dimension(:), intent(in) :: locconc + real(DP), intent(in) :: viscref + real(DP), dimension(:), intent(in) :: dviscdc + real(DP), dimension(:), intent(in) :: cviscref + real(DP), dimension(:), intent(inout) :: ctemp + real(DP), dimension(:, :), intent(in) :: auxvar + ! -- return + real(DP) :: viscbnd + ! -- local + integer(I4B) :: i +! ------------------------------------------------------------------------------ + ! + ! -- assign boundary viscosity based on one of three options + if (locvisc > 0) then + ! -- assign viscosity to an aux column named 'VISCOSITY' + viscbnd = auxvar(locvisc, n) + else if (locconc(1) > 0) then + ! -- calculate viscosity using one or more concentration auxcolumns + do i = 1, size(locconc) + ctemp(i) = DZERO + if (locconc(i) > 0) then + ctemp(i) = auxvar(locconc(i), n) + end if + end do + viscbnd = calc_visc(ivisc, viscref, dviscdc, cviscref, ctemp, a2, a3, a4) + else + ! -- neither of the above, so assign as viscref + viscbnd = viscref + end if + ! + ! -- return + return + end function calc_bnd_viscosity + + !> @brief hit the hydraulic conductivity values with the ratio mu_o/mu + !! + !! This routine called after updating the viscosity values using the latest + !! concentration and/or temperature values. The ratio mu_o/mu, reference + !! viscosity divided by the updated viscosity value, is multiplied by K + !! for each cell. + !< + subroutine update_k_with_vsc(this) +! +! SPECIFICATIONS: +! ------------------------------------------------------------------------------ + ! -- modules + ! -- dummy + class(GwfVscType) :: this + integer(I4B) :: n +! ------------------------------------------------------------------------------ + ! + ! -- For viscosity-based K's, apply change of K to K11 by starting with + ! user-specified K values and not the K's leftover from the last viscosity + ! update. + do n = 1, this%dis%nodes + this%k11(n) = this%k11input(n) * (this%viscref / this%visc(n)) + if (this%ik22 /= 0) then + this%k22(n) = this%k22input(n) * (this%viscref / this%visc(n)) + end if + if (this%ik33 /= 0) then + this%k33(n) = this%k33input(n) * (this%viscref / this%visc(n)) + end if + this%nodekchange(n) = 1 + end do + ! + ! -- Flag kchange + call this%vsc_set_changed_at(kper, kstp) + ! + ! -- return + return + end subroutine update_k_with_vsc + + !> @brief Mark K changes as having occurred at (kper, kstp) + !! + !! Procedure called by VSC code when K updated due to viscosity changes. + !! K values changed at (kper, kstp). + !! + !< + subroutine vsc_set_changed_at(this, kper, kstp) + ! -- dummy variables + class(GwfVscType) :: this + integer(I4B), intent(in) :: kper + integer(I4B), intent(in) :: kstp + ! + this%kchangeper = kper + this%kchangestp = kstp + ! + return + end subroutine vsc_set_changed_at + + subroutine vsc_ot_dv(this, idvfl) +! ****************************************************************************** +! vsc_ot_dv -- Save calculated viscosity array to binary file +! ****************************************************************************** +! +! SPECIFICATIONS: +! ------------------------------------------------------------------------------ + ! -- dummy + class(GwfVscType) :: this + integer(I4B), intent(in) :: idvfl + ! -- local + character(len=1) :: cdatafmp = ' ', editdesc = ' ' + integer(I4B) :: ibinun + integer(I4B) :: iprint + integer(I4B) :: nvaluesp + integer(I4B) :: nwidthp + real(DP) :: dinact +! ------------------------------------------------------------------------------ + ! + ! -- Set unit number for viscosity output + if (this%ioutvisc /= 0) then + ibinun = 1 + else + ibinun = 0 + end if + if (idvfl == 0) ibinun = 0 + ! + ! -- save viscosity array + if (ibinun /= 0) then + iprint = 0 + dinact = DHNOFLO + ! + ! -- write viscosity to binary file + if (this%ioutvisc /= 0) then + ibinun = this%ioutvisc + call this%dis%record_array(this%visc, this%iout, iprint, ibinun, & + ' VISCOSITY', cdatafmp, nvaluesp, & + nwidthp, editdesc, dinact) + end if + end if + ! + ! -- Return + return + end subroutine vsc_ot_dv + + subroutine vsc_da(this) +! ****************************************************************************** +! vsc_da -- Deallocate +! ****************************************************************************** +! +! SPECIFICATIONS: +! ------------------------------------------------------------------------------ + ! -- modules + ! -- dummy + class(GwfVscType) :: this +! ------------------------------------------------------------------------------ + ! + ! -- Deallocate arrays if package was active + if (this%inunit > 0) then + call mem_deallocate(this%visc) + call mem_deallocate(this%ivisc) + call mem_deallocate(this%concvsc) + call mem_deallocate(this%dviscdc) + call mem_deallocate(this%cviscref) + call mem_deallocate(this%ctemp) + deallocate (this%cmodelname) + deallocate (this%cauxspeciesname) + deallocate (this%modelconc) + end if + ! + ! -- Scalars + call mem_deallocate(this%thermivisc) + call mem_deallocate(this%idxtmpr) + call mem_deallocate(this%ioutvisc) + call mem_deallocate(this%ireadelev) + call mem_deallocate(this%iconcset) + call mem_deallocate(this%viscref) + call mem_deallocate(this%nviscspecies) + call mem_deallocate(this%a2) + call mem_deallocate(this%a3) + call mem_deallocate(this%a4) + ! + ! -- Nullify pointers to other package variables + nullify (this%ik22overk) + nullify (this%ik33overk) + nullify (this%k11) + nullify (this%k22) + nullify (this%k33) + nullify (this%k11input) + nullify (this%k22input) + nullify (this%k33input) + nullify (this%ik22) + nullify (this%ik33) + nullify (this%kchangeper) + nullify (this%kchangestp) + nullify (this%nodekchange) + ! + ! -- deallocate parent + call this%NumericalPackageType%da() + ! + ! -- Return + return + end subroutine vsc_da + + subroutine read_dimensions(this) +! ****************************************************************************** +! read_dimensions -- Read the dimensions for this package +! ****************************************************************************** +! +! SPECIFICATIONS: +! ------------------------------------------------------------------------------ + ! -- modules + ! -- dummy + class(GwfVscType), intent(inout) :: this + ! -- local + character(len=LINELENGTH) :: errmsg, keyword + integer(I4B) :: ierr + logical :: isfound, endOfBlock + ! -- format +! ------------------------------------------------------------------------------ + ! + ! -- get dimensions block + call this%parser%GetBlock('DIMENSIONS', isfound, ierr, & + supportOpenClose=.true.) + ! + ! -- parse dimensions block if detected + if (isfound) then + write (this%iout, '(/1x,a)') 'PROCESSING VSC DIMENSIONS' + do + call this%parser%GetNextLine(endOfBlock) + if (endOfBlock) exit + call this%parser%GetStringCaps(keyword) + select case (keyword) + case ('NVISCSPECIES') + this%nviscspecies = this%parser%GetInteger() + write (this%iout, '(4x,a,i0)') 'NVISCSPECIES = ', this%nviscspecies + case default + write (errmsg, '(4x,a,a)') & + 'UNKNOWN VSC DIMENSION: ', trim(keyword) + call store_error(errmsg) + call this%parser%StoreErrorUnit() + end select + end do + write (this%iout, '(1x,a)') 'END OF VSC DIMENSIONS' + else + call store_error('REQUIRED VSC DIMENSIONS BLOCK NOT FOUND.') + call this%parser%StoreErrorUnit() + end if + ! + ! -- check dimension + if (this%nviscspecies < 1) then + call store_error('NVISCSPECIES MUST BE GREATER THAN ZERO.') + call this%parser%StoreErrorUnit() + end if + ! + ! -- return + return + end subroutine read_dimensions + + !> @ brief Read data for package + !! + !! Method to read data for the package. + !! + !< + subroutine read_packagedata(this) + ! -- modules + ! -- dummy + class(GwfVscType) :: this + ! -- local + character(len=LINELENGTH) :: warnmsg, errmsg + character(len=LINELENGTH) :: line + character(len=LENMODELNAME) :: mname + integer(I4B) :: ierr + integer(I4B) :: im + integer(I4B) :: iviscspec + logical :: isfound, endOfBlock + logical :: blockrequired + integer(I4B), dimension(:), allocatable :: itemp + character(len=10) :: c10 + character(len=16) :: c16 + ! -- format + character(len=*), parameter :: fmterr = & + "('INVALID VALUE FOR IRHOSPEC (',i0,') DETECTED IN VSC PACKAGE. & + &IRHOSPEC MUST BE > 0 AND <= NVISCSPECIES, AND DUPLICATE VALUES & + &ARE NOT ALLOWED.')" +! ------------------------------------------------------------------------------ + ! + ! -- initialize + allocate (itemp(this%nviscspecies)) + itemp(:) = 0 + ! + ! -- get packagedata block + blockrequired = .true. + call this%parser%GetBlock('PACKAGEDATA', isfound, ierr, & + blockRequired=blockRequired, & + supportOpenClose=.true.) + ! + ! -- parse packagedata block + if (isfound) then + write (this%iout, '(1x,a)') 'PROCESSING VSC PACKAGEDATA' + do + call this%parser%GetNextLine(endOfBlock) + if (endOfBlock) exit + iviscspec = this%parser%GetInteger() + if (iviscspec < 1 .or. iviscspec > this%nviscspecies) then + write (errmsg, fmterr) iviscspec + call store_error(errmsg) + end if + if (itemp(iviscspec) /= 0) then + write (errmsg, fmterr) iviscspec + call store_error(errmsg) + end if + itemp(iviscspec) = 1 + ! + this%dviscdc(iviscspec) = this%parser%GetDouble() + this%cviscref(iviscspec) = this%parser%GetDouble() + call this%parser%GetStringCaps(this%cmodelname(iviscspec)) + call this%parser%GetStringCaps(this%cauxspeciesname(iviscspec)) + ! + if (this%cauxspeciesname(iviscspec) == 'TEMPERATURE') then + if (this%idxtmpr > 0) then + write (errmsg, '(a)') 'MORE THAN ONE SPECIES IN VSC INPUT IDENTIFIED & + &AS "TEMPERATURE". ONLY ONE SPECIES MAY BE DESIGNATED AS & + &TEMPERATURE.' + call store_error(errmsg) + else + this%idxtmpr = iviscspec + if (this%thermivisc == 2) then + this%ivisc(iviscspec) = 2 + end if + end if + end if + end do + end if + ! + ! -- Check for errors. + if (count_errors() > 0) then + call this%parser%StoreErrorUnit() + end if + ! + ! -- write packagedata information + write (this%iout, '(/,1x,a)') 'SUMMARY OF SPECIES INFORMATION IN VSC PACKAGE' + write (this%iout, '(1a11,5a17)') & + 'SPECIES', 'DVISCDC', 'CVISCREF', 'MODEL', 'AUXSPECIESNAME' + do iviscspec = 1, this%nviscspecies + write (c10, '(i0)') iviscspec + line = ' '//adjustr(c10) + + write (c16, '(g15.6)') this%dviscdc(iviscspec) + line = trim(line)//' '//adjustr(c16) + write (c16, '(g15.6)') this%cviscref(iviscspec) + line = trim(line)//' '//adjustr(c16) + write (c16, '(a)') this%cmodelname(iviscspec) + line = trim(line)//' '//adjustr(c16) + write (c16, '(a)') this%cauxspeciesname(iviscspec) + line = trim(line)//' '//adjustr(c16) + write (this%iout, '(a)') trim(line) + end do + ! + ! -- deallocate + deallocate (itemp) + ! + write (this%iout, '(/,1x,a)') 'END OF VSC PACKAGEDATA' + ! + ! -- return + return + end subroutine read_packagedata + + !> @brief Sets package data instead of reading from file + !< + subroutine set_packagedata(this, input_data) + class(GwfVscType) :: this !< this vscoancy pkg + type(GwfVscInputDataType), intent(in) :: input_data !< the input data to be set + ! local + integer(I4B) :: ispec + + do ispec = 1, this%nviscspecies + this%dviscdc(ispec) = input_data%dviscdc(ispec) + this%cviscref(ispec) = input_data%cviscref(ispec) + this%cmodelname(ispec) = input_data%cmodelname(ispec) + this%cauxspeciesname(ispec) = input_data%cauxspeciesname(ispec) + end do + + end subroutine set_packagedata + + subroutine vsc_calcvisc(this) +! ****************************************************************************** +! vsc_calcvisc -- calculate fluid viscosity from concentration +! ****************************************************************************** +! +! SPECIFICATIONS: +! ------------------------------------------------------------------------------ + ! -- dummy + class(GwfVscType) :: this + + ! -- local + integer(I4B) :: n + integer(I4B) :: i +! ------------------------------------------------------------------------------ + ! + ! -- Calculate the viscosity using the specified concentration and/or + ! temperature arrays + do n = 1, this%dis%nodes + do i = 1, this%nviscspecies + if (this%modelconc(i)%icbund(n) == 0) then + this%ctemp = DZERO + else + this%ctemp(i) = this%modelconc(i)%conc(n) + end if + end do + ! + ! -- Call function corresponding to (1) temperature or (2) concentration + !if (i == this%idxtmpr) then + ! Temperature + !this%visc(n) = this%visc(n) + calc_visc_t(this%viscref, this%dviscdc, & + ! this%cviscref, this%ctemp, & + ! this% + !else + ! Concentration + this%visc(n) = calc_visc(this%ivisc, this%viscref, this%dviscdc, & + this%cviscref, this%ctemp, this%a2, & + this%a3, this%a4) + !end if + + end do + ! + ! -- Return + return + end subroutine vsc_calcvisc + + subroutine allocate_scalars(this) +! ****************************************************************************** +! allocate_scalars +! ****************************************************************************** +! +! SPECIFICATIONS: +! ------------------------------------------------------------------------------ + ! -- modules + use ConstantsModule, only: DZERO + ! -- dummy + class(GwfVscType) :: this + ! -- local +! ------------------------------------------------------------------------------ + ! + ! -- allocate scalars in NumericalPackageType + call this%NumericalPackageType%allocate_scalars() + ! + ! -- Allocate + call mem_allocate(this%thermivisc, 'THERMIVISC', this%memoryPath) + call mem_allocate(this%idxtmpr, 'IDXTMPR', this%memoryPath) + call mem_allocate(this%ioutvisc, 'IOUTVISC', this%memoryPath) + call mem_allocate(this%ireadelev, 'IREADELEV', this%memoryPath) + call mem_allocate(this%iconcset, 'ICONCSET', this%memoryPath) + call mem_allocate(this%viscref, 'VISCREF', this%memoryPath) + call mem_allocate(this%a2, 'A2', this%memoryPath) + call mem_allocate(this%a3, 'A3', this%memoryPath) + call mem_allocate(this%a4, 'A4', this%memoryPath) + ! + call mem_allocate(this%nviscspecies, 'NVISCSPECIES', this%memoryPath) + ! + ! -- Initialize + this%thermivisc = 0 + this%idxtmpr = 0 + this%ioutvisc = 0 + this%ireadelev = 0 + this%iconcset = 0 + this%viscref = 1000.d0 + this%A2 = DZERO + this%A3 = DZERO + this%A4 = DZERO + ! + this%nviscspecies = 0 + ! + ! -- Return + return + end subroutine allocate_scalars + + subroutine allocate_arrays(this, nodes) +! ****************************************************************************** +! allocate_arrays +! ****************************************************************************** +! +! SPECIFICATIONS: +! ------------------------------------------------------------------------------ + ! -- modules + ! -- dummy + class(GwfVscType) :: this + integer(I4B), intent(in) :: nodes + ! -- local + integer(I4B) :: i +! ------------------------------------------------------------------------------ + ! + ! -- Allocate + call mem_allocate(this%visc, nodes, 'VISC', this%memoryPath) + call mem_allocate(this%concvsc, 0, 'CONCVSC', this%memoryPath) + call mem_allocate(this%ivisc, this%nviscspecies, 'IVISC', this%memoryPath) + call mem_allocate(this%dviscdc, this%nviscspecies, 'DRHODC', & + this%memoryPath) + call mem_allocate(this%cviscref, this%nviscspecies, 'CRHOREF', & + this%memoryPath) + call mem_allocate(this%ctemp, this%nviscspecies, 'CTEMP', this%memoryPath) + allocate (this%cmodelname(this%nviscspecies)) + allocate (this%cauxspeciesname(this%nviscspecies)) + allocate (this%modelconc(this%nviscspecies)) + ! + ! -- Initialize + do i = 1, nodes + this%visc(i) = this%viscref + end do + ! + ! -- Initialize nviscspecies arrays + do i = 1, this%nviscspecies + this%ivisc(i) = 1 + this%dviscdc(i) = DZERO + this%cviscref(i) = DZERO + this%ctemp(i) = DZERO + this%cmodelname(i) = '' + this%cauxspeciesname(i) = '' + end do + ! + ! -- Return + return + end subroutine allocate_arrays + + subroutine read_options(this) +! ****************************************************************************** +! read_options +! ****************************************************************************** +! +! SPECIFICATIONS: +! ------------------------------------------------------------------------------ + ! -- modules + use OpenSpecModule, only: access, form + use InputOutputModule, only: urword, getunit, urdaux, openfile + ! -- dummy + class(GwfVscType) :: this + ! -- local + character(len=LINELENGTH) :: warnmsg, errmsg, keyword, keyword2 + character(len=MAXCHARLEN) :: fname + character(len=LINELENGTH) :: line + character(len=10) :: c10 + character(len=16) :: c16 + integer(I4B) :: ierr + logical :: isfound, endOfBlock + ! -- formats + character(len=*), parameter :: fmtfileout = & + "(x, 'VSC', 1x, a, 1x, 'WILL BE SAVED TO FILE: ', & + &a, /4x, 'OPENED ON UNIT: ', I7)" + character(len=*), parameter :: fmtlinear = & + "(/,x,'VISCOSITY WILL VARY LINEARLY WITH TEMPERATURE & + &CHANGE ')" + character(len=*), parameter :: fmtnonlinear = & + "(/,x,'VISCOSITY WILL VARY NON-LINEARLY WITH TEMPERATURE & + &CHANGE ')" +! ------------------------------------------------------------------------------ + ! + ! -- get options block + call this%parser%GetBlock('OPTIONS', isfound, ierr, & + supportOpenClose=.true., blockRequired=.false.) + ! + ! -- parse options block if detected + if (isfound) then + write (this%iout, '(1x,a)') 'PROCESSING VSC OPTIONS' + do + call this%parser%GetNextLine(endOfBlock) + if (endOfBlock) exit + call this%parser%GetStringCaps(keyword) + select case (keyword) + case ('VISCREF') + this%viscref = this%parser%GetDouble() + write (this%iout, '(4x,a,1pg15.6)') & + 'REFERENCE VISCOSITY HAS BEEN SET TO: ', & + this%viscref + case ('VISCOSITY') + call this%parser%GetStringCaps(keyword) + if (keyword == 'FILEOUT') then + call this%parser%GetString(fname) + this%ioutvisc = getunit() + call openfile(this%ioutvisc, this%iout, fname, 'DATA(BINARY)', & + form, access, 'REPLACE') + write (this%iout, fmtfileout) & + 'VISCOSITY', fname, this%ioutvisc + else + errmsg = 'OPTIONAL VISCOSITY KEYWORD MUST BE '// & + 'FOLLOWED BY FILEOUT' + call store_error(errmsg) + end if + case ('THERMAL_VISCOSITY_FUNC') + call this%parser%GetStringCaps(keyword2) + if (trim(adjustl(keyword2)) == 'LINEAR') this%thermivisc = 1 + if (trim(adjustl(keyword2)) == 'NONLINEAR') this%thermivisc = 2 + select case (this%thermivisc) + case (1) + write (this%iout, fmtlinear) + case (2) + write (this%iout, fmtnonlinear) + this%a2 = this%parser%GetDouble() + this%a3 = this%parser%GetDouble() + this%a4 = this%parser%GetDouble() + ! + ! -- Write viscosity function selection to lst file + write (this%iout, '(/,x,a,a,a)') 'CONSTANTS USED IN ', & + trim(keyword2), ' VISCOSITY FORMULATION ARE ' + write (this%iout, '(x,a)') & + ' A2, A3, A4' + line = ' ' + write (c16, '(g15.6)') this%a2 + line = trim(line)//' '//adjustr(c16) + write (c16, '(g15.6)') this%a3 + line = trim(line)//' '//adjustr(c16) + write (c16, '(g15.6)') this%a4 + line = trim(line)//' '//adjustr(c16) + write (this%iout, '(a)') trim(line) + + end select + case default + write (errmsg, '(4x,a,a)') '**ERROR. UNKNOWN VSC OPTION: ', & + trim(keyword) + call store_error(errmsg) + call this%parser%StoreErrorUnit() + end select + end do + ! + if (this%thermivisc == 1) then + if (this%a2 == 0.0) then + write (errmsg, '(a)') 'LINEAR OPTION SELECTED FOR VARYING & + &VISCOSITY WITH TEMPERTURE, BUT A1, A SURROGATE FOR & + &dVISC/dT, SET EQUAL TO 0.0' + call store_error(errmsg) + end if + end if + if (this%thermivisc > 1) then + if (this%a2 == 0) then + write (warnmsg, '(a)') 'NONLINEAR OPTION SELECTED FOR & + &VARYING VISCOSITY WITH TEMPERATURE, BUT A2 SET EQUAL TO & + &ZERO WHICH MAY LEAD TO UNINTENDED VALUES FOR VISCOSITY' + call store_warning(errmsg) + end if + if (this%a3 == 0) then + write (warnmsg, '(a)') 'NONLINEAR OPTION SELECTED FOR & + &VARYING VISCOSITY WITH TEMPERATURE, BUT A3 SET EQUAL TO & + &ZERO WHICH MAY LEAD TO UNINTENDED VALUES FOR VISCOSITY' + call store_warning(warnmsg) + end if + if (this%a4 == 0) then + write (warnmsg, '(a)') 'NONLINEAR OPTION SELECTED FOR & + &VARYING VISCOSITY WITH TEMPERATURE, BUT A4 SET EQUAL TO & + &ZERO WHICH MAY LEAD TO UNINTENDED VALUES FOR VISCOSITY' + call store_warning(warnmsg) + end if + end if + end if + ! + write (this%iout, '(/,x,a)') 'END OF VSC OPTIONS' + ! + ! -- Return + return + end subroutine read_options + + !> @brief Sets options as opposed to reading them from a file + !< + subroutine set_options(this, input_data) + class(GwfVscType) :: this + type(GwfVscInputDataType), intent(in) :: input_data !< the input data to be set + + this%viscref = input_data%viscref + ! + ! -- Return + return + end subroutine set_options + + subroutine set_concentration_pointer(this, modelname, conc, icbund, istmpr) +! ****************************************************************************** +! set_concentration_pointer -- pass in a gwt model name, concentration array +! and ibound, and store a pointer to these in the VSC package so that +! viscosity can be calculated from them. +! This routine is called from the gwfgwt exchange in the exg_ar() method. +! ****************************************************************************** +! +! SPECIFICATIONS: +! ------------------------------------------------------------------------------ + ! -- modules + ! -- dummy + class(GwfVscType) :: this + character(len=LENMODELNAME), intent(in) :: modelname + real(DP), dimension(:), pointer :: conc + integer(I4B), dimension(:), pointer :: icbund + integer(I4B), optional, intent(in) :: istmpr + ! -- local + integer(I4B) :: i + logical :: found +! ------------------------------------------------------------------------------ + ! + this%iconcset = 1 + found = .false. + do i = 1, this%nviscspecies + if (this%cmodelname(i) == modelname) then + this%modelconc(i)%conc => conc + this%modelconc(i)%icbund => icbund + found = .true. + exit + end if + end do + ! + ! -- Return + return + end subroutine set_concentration_pointer + +end module GwfVscModule diff --git a/src/Model/ModelUtilities/BoundaryPackage.f90 b/src/Model/ModelUtilities/BoundaryPackage.f90 index c978db1bbf5..f9a83dbb649 100644 --- a/src/Model/ModelUtilities/BoundaryPackage.f90 +++ b/src/Model/ModelUtilities/BoundaryPackage.f90 @@ -80,9 +80,13 @@ module BndModule real(DP), dimension(:), pointer, contiguous :: simtomvr => null() !< simulated to mover values ! ! -- water mover flag and object - integer(I4B), pointer :: imover => null() !< flag indicating of the mover is active in the package + integer(I4B), pointer :: imover => null() !< flag indicating if the mover is active in the package type(PackageMoverType), pointer :: pakmvrobj => null() !< mover object for package ! + ! -- viscosity flag and safe-copy of conductance array + integer(I4B), pointer :: ivsc => null() !< flag indicating if viscosity is active in the model + real(DP), dimension(:), pointer, contiguous :: condinput => null() !< stores user-specified conductance values + ! ! -- timeseries type(TimeSeriesManagerType), pointer :: TsManager => null() !< time series manager type(TimeArraySeriesManagerType), pointer :: TasManager => null() !< time array series manager @@ -152,6 +156,9 @@ module BndModule ! -- procedure to support time series procedure, public :: bnd_rp_ts ! + ! -- procedure to backup user-specified conductance + procedure, private :: bnd_store_user_cond + ! end type BndType contains @@ -362,6 +369,12 @@ subroutine bnd_rp(this) this%packName, this%tsManager, this%iscloc) this%nbound = nlist ! + ! -- save user-specified conductance if vsc package is active + if (this%ivsc == 1) then + call this%bnd_store_user_cond(nlist, this%nodelist, this%bound, & + this%condinput) + end if + ! ! Define the tsLink%Text value(s) appropriately. ! E.g. for WEL package, entry 1, assign tsLink%Text = 'Q' ! For RIV package, entry 1 text = 'STAGE', entry 2 text = 'COND', @@ -902,6 +915,7 @@ subroutine bnd_da(this) call mem_deallocate(this%nodelist, 'NODELIST', this%memoryPath) call mem_deallocate(this%noupdateauxvar, 'NOUPDATEAUXVAR', this%memoryPath) call mem_deallocate(this%bound, 'BOUND', this%memoryPath) + call mem_deallocate(this%condinput, 'CONDINPUT', this%memoryPath) call mem_deallocate(this%hcof, 'HCOF', this%memoryPath) call mem_deallocate(this%rhs, 'RHS', this%memoryPath) call mem_deallocate(this%simvals, 'SIMVALS', this%memoryPath) @@ -958,6 +972,7 @@ subroutine bnd_da(this) call mem_deallocate(this%imover) call mem_deallocate(this%npakeq) call mem_deallocate(this%ioffset) + call mem_deallocate(this%ivsc) ! ! -- deallocate methods on objects call this%obs%obs_da() @@ -1016,6 +1031,9 @@ subroutine allocate_scalars(this) ! -- allocate the object and assign values to object variables call mem_allocate(this%imover, 'IMOVER', this%memoryPath) ! + ! -- allocate flag for determining if vsc active + call mem_allocate(this%ivsc, 'IVSC', this%memoryPath) + ! ! -- allocate scalars for packages that add rows to the matrix (e.g. MAW) call mem_allocate(this%npakeq, 'NPAKEQ', this%memoryPath) call mem_allocate(this%ioffset, 'IOFFSET', this%memoryPath) @@ -1043,6 +1061,7 @@ subroutine allocate_scalars(this) this%imover = 0 this%npakeq = 0 this%ioffset = 0 + this%ivsc = 0 ! ! -- Set pointer to model inewton variable call mem_setptr(imodelnewton, 'INEWTON', create_mem_path(this%name_model)) @@ -1092,6 +1111,17 @@ subroutine allocate_arrays(this, nodelist, auxvar) call mem_allocate(this%bound, this%ncolbnd, this%maxbound, 'BOUND', & this%memoryPath) ! + !-- Allocate array for storing user-specified conductances if vsc active + if (this%ivsc == 1) then + call mem_allocate(this%condinput, this%maxbound, 'CONDINPUT', & + this%memoryPath) + do i = 1, this%maxbound + this%condinput(i) = DZERO + end do + else + call mem_allocate(this%condinput, 0, 'CONDINPUT', this%memoryPath) + end if + ! ! -- Allocate hcof and rhs call mem_allocate(this%hcof, this%maxbound, 'HCOF', this%memoryPath) call mem_allocate(this%rhs, this%maxbound, 'RHS', this%memoryPath) @@ -1472,6 +1502,47 @@ subroutine bnd_read_dimensions(this) return end subroutine bnd_read_dimensions + !> @ brief Store user-specified conductances when vsc is active + !! + !! VSC will update boundary package conductance values. Because + !! viscosity can change every stress period, but user-specified + !! conductances may not, the base user-input should be stored in + !! backup array so that viscosity-updated conductances may be + !! recalculated every stress period/time step + !! + !< + subroutine bnd_store_user_cond(this, nlist, nodelist, rlist, condinput) + ! -- modules + use SimModule, only: store_error + ! -- dummy variables + class(BndType), intent(inout) :: this !< BndType object + integer(I4B), intent(in) :: nlist + integer(I4B), dimension(:), pointer, contiguous, intent(inout) :: nodelist + real(DP), dimension(:, :), pointer, contiguous, intent(in) :: rlist + real(DP), dimension(:), pointer, contiguous, intent(inout) :: condinput + ! -- local variables + integer(I4B) :: l + integer(I4B) :: nodeu, noder + character(len=LINELENGTH) :: nodestr + ! + ! -- store backup copy of conductance values + do l = 1, nlist + nodeu = nodelist(l) + noder = this%dis%get_nodenumber(nodeu, 0) + if (noder <= 0) then + call this%dis%nodeu_to_string(nodeu, nodestr) + write (errmsg, *) & + ' Cell is outside active grid domain: '// & + trim(adjustl(nodestr)) + call store_error(errmsg) + end if + condinput(l) = rlist(2, l) + end do + ! + ! -- return + return + end subroutine + !> @ brief Read initial parameters for package !! !! Read initial parameters for a boundary package. This method is not diff --git a/src/Model/ModelUtilities/GwfVscInputData.f90 b/src/Model/ModelUtilities/GwfVscInputData.f90 new file mode 100644 index 00000000000..9786d78bea5 --- /dev/null +++ b/src/Model/ModelUtilities/GwfVscInputData.f90 @@ -0,0 +1,54 @@ +module GwfVscInputDataModule + use KindModule, only: I4B, DP + use ConstantsModule, only: LENMODELNAME, LENAUXNAME, DZERO + + implicit none + private + + !> Data structure to transfer input configuration to the + !< VSC package, as opposed to reading from file + type, public :: GwfVscInputDataType + + ! options + real(DP) :: viscref !< see VSC for description + ! dim + integer(I4B) :: nviscspecies !< see VSC for description + ! pkg data + real(DP), dimension(:), pointer, contiguous :: dviscdc => null() !< see VSC for description + real(DP), dimension(:), pointer, contiguous :: cviscref => null() !< see VSC for description + character(len=LENMODELNAME), dimension(:), allocatable :: cmodelname !< see VSC for description + character(len=LENAUXNAME), dimension(:), allocatable :: cauxspeciesname !< see VSC for description + + contains + procedure, pass(this) :: construct + procedure, pass(this) :: destruct + end type GwfVscInputDataType + +contains + +!> @brief Allocate the input data +!< + subroutine construct(this, nviscspecies) + class(GwfVscInputDataType) :: this !< the input data block + integer(I4B) :: nviscspecies !< the number of species + + allocate (this%dviscdc(nviscspecies)) + allocate (this%cviscref(nviscspecies)) + allocate (this%cmodelname(nviscspecies)) + allocate (this%cauxspeciesname(nviscspecies)) + + end subroutine construct + + !> @brief clean up + !< + subroutine destruct(this) + class(GwfVscInputDataType) :: this !< the input data block + + deallocate (this%dviscdc) + deallocate (this%cviscref) + deallocate (this%cmodelname) + deallocate (this%cauxspeciesname) + + end subroutine destruct + +end module GwfVscInputDataModule From e94362e2afb7ee00b890fc21f79ee7f2a2ef93b5 Mon Sep 17 00:00:00 2001 From: Eric Morway Date: Tue, 25 Oct 2022 15:05:44 -0700 Subject: [PATCH 02/31] forgot about meson --- src/meson.build | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/meson.build b/src/meson.build index f5df942650a..a8d55285cc6 100644 --- a/src/meson.build +++ b/src/meson.build @@ -34,6 +34,7 @@ modflow_sources = files( 'Model' / 'GroundWaterFlow' / 'gwf3.f90', 'Model' / 'GroundWaterFlow' / 'gwf3api8.f90', 'Model' / 'GroundWaterFlow' / 'gwf3buy8.f90', + 'Model' / 'GroundWaterFlow' / 'gwf3vsc8.f90', 'Model' / 'GroundWaterFlow' / 'gwf3chd8.f90', 'Model' / 'GroundWaterFlow' / 'gwf3csub8.f90', 'Model' / 'GroundWaterFlow' / 'gwf3dis8.f90', @@ -90,6 +91,7 @@ modflow_sources = files( 'Model' / 'ModelUtilities' / 'GwfMvrPeriodData.f90', 'Model' / 'ModelUtilities' / 'GwfNpfOptions.f90', 'Model' / 'ModelUtilities' / 'GwfStorageUtils.f90', + 'Model' / 'ModelUtilities' / 'GwfVscInputData.f90,' 'Model' / 'ModelUtilities' / 'GwtAdvOptions.f90', 'Model' / 'ModelUtilities' / 'GwtDspOptions.f90', 'Model' / 'ModelUtilities' / 'GwtSpc.f90', From 57436cce8396b0beb06544f0e6d9a847a93c943d Mon Sep 17 00:00:00 2001 From: Eric Morway Date: Tue, 25 Oct 2022 15:33:30 -0700 Subject: [PATCH 03/31] meson --- src/meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/meson.build b/src/meson.build index a8d55285cc6..a922990a2c8 100644 --- a/src/meson.build +++ b/src/meson.build @@ -91,7 +91,7 @@ modflow_sources = files( 'Model' / 'ModelUtilities' / 'GwfMvrPeriodData.f90', 'Model' / 'ModelUtilities' / 'GwfNpfOptions.f90', 'Model' / 'ModelUtilities' / 'GwfStorageUtils.f90', - 'Model' / 'ModelUtilities' / 'GwfVscInputData.f90,' + 'Model' / 'ModelUtilities' / 'GwfVscInputData.f90', 'Model' / 'ModelUtilities' / 'GwtAdvOptions.f90', 'Model' / 'ModelUtilities' / 'GwtDspOptions.f90', 'Model' / 'ModelUtilities' / 'GwtSpc.f90', From 7b4a88e5fb8a29a17a41b65e94c786ebb376dd45 Mon Sep 17 00:00:00 2001 From: Eric Morway Date: Tue, 25 Oct 2022 16:35:42 -0700 Subject: [PATCH 04/31] attempt to fix some GNU compile errors --- src/Model/GroundWaterFlow/gwf3vsc8.f90 | 42 +++++--------------- src/Model/ModelUtilities/GwfVscInputData.f90 | 3 ++ 2 files changed, 14 insertions(+), 31 deletions(-) diff --git a/src/Model/GroundWaterFlow/gwf3vsc8.f90 b/src/Model/GroundWaterFlow/gwf3vsc8.f90 index d3846731441..7c357176ad7 100644 --- a/src/Model/GroundWaterFlow/gwf3vsc8.f90 +++ b/src/Model/GroundWaterFlow/gwf3vsc8.f90 @@ -35,12 +35,11 @@ module GwfVscModule integer(I4B), dimension(:), pointer :: ivisc => null() !< viscosity formulation flag for each species (1:Linear, 2:Nonlinear) real(DP), pointer :: viscref => null() !< reference fluid viscosity real(DP), dimension(:), pointer, contiguous :: visc => null() !< viscosity - real(DP), dimension(:), pointer, contiguous :: concvsc => null() !< concentration (or temperature) array if specified in vsc package ! kluge note: is this ever really used? real(DP), dimension(:), pointer, contiguous :: elev => null() !< cell center elevation (optional; if not specified, then use (top+bot)/2) integer(I4B), dimension(:), pointer :: ibound => null() !< store pointer to ibound integer(I4B), pointer :: nviscspecies => null() !< number of concentration species used in viscosity equation - real(DP), dimension(:), pointer, contiguous :: dviscdc => null() !< change in viscosity with change in concentration ! kluge note: parameters will depend on formula; linear for now + real(DP), dimension(:), pointer, contiguous :: dviscdc => null() !< linear change in viscosity with change in concentration real(DP), dimension(:), pointer, contiguous :: cviscref => null() !< reference concentration used in viscosity equation real(DP), dimension(:), pointer, contiguous :: ctemp => null() !< temporary array of size (nviscspec) to pass to calc_visc_x character(len=LENMODELNAME), dimension(:), allocatable :: cmodelname !< names of gwt (or gwe) models used in viscosity equation @@ -123,11 +122,13 @@ function calc_visc(ivisc, viscref, dviscdc, cviscref, conc, & do i = 1, nviscspec if (ivisc(i) == 1) then - visc = visc + dviscdc(i) * (conc(i) - cviscref(i)) ! kluge note: linear for now + visc = visc + dviscdc(i) * (conc(i) - cviscref(i)) else expon = -1 * a3 * ((conc(i) - cviscref(i)) / & ((conc(i) + a4) * (cviscref(i) + a4))) mu_t = viscref * a2**expon + ! Order matters!! (This assumes we apply the temperature correction after + ! accounting for solute concentrations) ! If a nonlinear correction is applied, then b/c it takes into account ! viscref, need to subtract it in this case ! At most, there will only ever be 1 nonlinear correction @@ -135,16 +136,6 @@ function calc_visc(ivisc, viscref, dviscdc, cviscref, conc, & end if ! end if end do - - ! NOTES (from in-person meeting with Alden) - ! Order matters!! (This assumes we apply the temperature correction after - ! accounting for solute concentrations) - ! REMEMBER: idxtmpr - ! For the case i == idxtmpr - ! special multiplicative eqn here that leverages idxtmpr - ! - check to make sure that idxtmpr is not zero b/c that means there - ! is no temperature (remember to initialize idxtmpr to 0) - ! ! -- return return @@ -201,7 +192,7 @@ subroutine vsc_df(this, dis, vsc_input) ! -- local ! -- formats character(len=*), parameter :: fmtvsc = & - "(1x,/1x,'VSC -- VISCOSITY PACKAGE, VERSION 1, 9/30/2023', & + "(1x,/1x,'VSC -- VISCOSITY PACKAGE, VERSION 1, 10/30/2022', & &' INPUT READ FROM UNIT ', i0, //)" ! ------------------------------------------------------------------------------ ! @@ -944,7 +935,6 @@ subroutine vsc_da(this) if (this%inunit > 0) then call mem_deallocate(this%visc) call mem_deallocate(this%ivisc) - call mem_deallocate(this%concvsc) call mem_deallocate(this%dviscdc) call mem_deallocate(this%cviscref) call mem_deallocate(this%ctemp) @@ -1194,18 +1184,9 @@ subroutine vsc_calcvisc(this) end if end do ! - ! -- Call function corresponding to (1) temperature or (2) concentration - !if (i == this%idxtmpr) then - ! Temperature - !this%visc(n) = this%visc(n) + calc_visc_t(this%viscref, this%dviscdc, & - ! this%cviscref, this%ctemp, & - ! this% - !else - ! Concentration this%visc(n) = calc_visc(this%ivisc, this%viscref, this%dviscdc, & this%cviscref, this%ctemp, this%a2, & this%a3, this%a4) - !end if end do ! @@ -1277,7 +1258,6 @@ subroutine allocate_arrays(this, nodes) ! ! -- Allocate call mem_allocate(this%visc, nodes, 'VISC', this%memoryPath) - call mem_allocate(this%concvsc, 0, 'CONCVSC', this%memoryPath) call mem_allocate(this%ivisc, this%nviscspecies, 'IVISC', this%memoryPath) call mem_allocate(this%dviscdc, this%nviscspecies, 'DRHODC', & this%memoryPath) @@ -1329,13 +1309,13 @@ subroutine read_options(this) logical :: isfound, endOfBlock ! -- formats character(len=*), parameter :: fmtfileout = & - "(x, 'VSC', 1x, a, 1x, 'WILL BE SAVED TO FILE: ', & + "(1x, 'VSC', 1x, a, 1x, 'WILL BE SAVED TO FILE: ', & &a, /4x, 'OPENED ON UNIT: ', I7)" character(len=*), parameter :: fmtlinear = & - "(/,x,'VISCOSITY WILL VARY LINEARLY WITH TEMPERATURE & + "(/,1x,'VISCOSITY WILL VARY LINEARLY WITH TEMPERATURE & &CHANGE ')" character(len=*), parameter :: fmtnonlinear = & - "(/,x,'VISCOSITY WILL VARY NON-LINEARLY WITH TEMPERATURE & + "(/,1x,'VISCOSITY WILL VARY NON-LINEARLY WITH TEMPERATURE & &CHANGE ')" ! ------------------------------------------------------------------------------ ! @@ -1384,9 +1364,9 @@ subroutine read_options(this) this%a4 = this%parser%GetDouble() ! ! -- Write viscosity function selection to lst file - write (this%iout, '(/,x,a,a,a)') 'CONSTANTS USED IN ', & + write (this%iout, '(/,1x,a,a,a)') 'CONSTANTS USED IN ', & trim(keyword2), ' VISCOSITY FORMULATION ARE ' - write (this%iout, '(x,a)') & + write (this%iout, '(1x,a)') & ' A2, A3, A4' line = ' ' write (c16, '(g15.6)') this%a2 @@ -1436,7 +1416,7 @@ subroutine read_options(this) end if end if ! - write (this%iout, '(/,x,a)') 'END OF VSC OPTIONS' + write (this%iout, '(/,1x,a)') 'END OF VSC OPTIONS' ! ! -- Return return diff --git a/src/Model/ModelUtilities/GwfVscInputData.f90 b/src/Model/ModelUtilities/GwfVscInputData.f90 index 9786d78bea5..fb39c0d1994 100644 --- a/src/Model/ModelUtilities/GwfVscInputData.f90 +++ b/src/Model/ModelUtilities/GwfVscInputData.f90 @@ -14,6 +14,7 @@ module GwfVscInputDataModule ! dim integer(I4B) :: nviscspecies !< see VSC for description ! pkg data + integer(I4B), dimension(:), pointer :: ivisc => null() !< indicates if species uses linear or nonlinear relationship real(DP), dimension(:), pointer, contiguous :: dviscdc => null() !< see VSC for description real(DP), dimension(:), pointer, contiguous :: cviscref => null() !< see VSC for description character(len=LENMODELNAME), dimension(:), allocatable :: cmodelname !< see VSC for description @@ -32,6 +33,7 @@ subroutine construct(this, nviscspecies) class(GwfVscInputDataType) :: this !< the input data block integer(I4B) :: nviscspecies !< the number of species + allocate (this%ivisc(nviscspecies)) allocate (this%dviscdc(nviscspecies)) allocate (this%cviscref(nviscspecies)) allocate (this%cmodelname(nviscspecies)) @@ -44,6 +46,7 @@ end subroutine construct subroutine destruct(this) class(GwfVscInputDataType) :: this !< the input data block + deallocate (this%ivisc) deallocate (this%dviscdc) deallocate (this%cviscref) deallocate (this%cmodelname) From 6e6d45e23d737dcb1905a6bc2deddb18ffae8ceb Mon Sep 17 00:00:00 2001 From: Eric Morway Date: Tue, 25 Oct 2022 17:07:49 -0700 Subject: [PATCH 05/31] not sure what the hang up is, grasping... --- src/Model/GroundWaterFlow/gwf3vsc8.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Model/GroundWaterFlow/gwf3vsc8.f90 b/src/Model/GroundWaterFlow/gwf3vsc8.f90 index 7c357176ad7..02c5ba1ea7c 100644 --- a/src/Model/GroundWaterFlow/gwf3vsc8.f90 +++ b/src/Model/GroundWaterFlow/gwf3vsc8.f90 @@ -32,7 +32,7 @@ module GwfVscModule integer(I4B), pointer :: ioutvisc => null() !< unit number for saving viscosity integer(I4B), pointer :: iconcset => null() !< if 1 then conc points to a gwt (or gwe) model%x array integer(I4B), pointer :: ireadelev => null() !< if 1 then elev has been allocated and filled - integer(I4B), dimension(:), pointer :: ivisc => null() !< viscosity formulation flag for each species (1:Linear, 2:Nonlinear) + integer(I4B), dimension(:), pointer, contiguous :: ivisc => null() !< viscosity formulation flag for each species (1:Linear, 2:Nonlinear) real(DP), pointer :: viscref => null() !< reference fluid viscosity real(DP), dimension(:), pointer, contiguous :: visc => null() !< viscosity real(DP), dimension(:), pointer, contiguous :: elev => null() !< cell center elevation (optional; if not specified, then use (top+bot)/2) From 5a84ea393dfd85cb05226749d440e241b245fee3 Mon Sep 17 00:00:00 2001 From: Eric Morway Date: Tue, 25 Oct 2022 17:28:27 -0700 Subject: [PATCH 06/31] removing stray (unused) variables in new vsc package --- src/Model/GroundWaterFlow/gwf3vsc8.f90 | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/Model/GroundWaterFlow/gwf3vsc8.f90 b/src/Model/GroundWaterFlow/gwf3vsc8.f90 index 02c5ba1ea7c..e7051233a76 100644 --- a/src/Model/GroundWaterFlow/gwf3vsc8.f90 +++ b/src/Model/GroundWaterFlow/gwf3vsc8.f90 @@ -524,7 +524,6 @@ subroutine vsc_ad_standard_bnd(packobj, hnew, visc, viscref, locelev, & integer(I4B) :: n integer(I4B) :: node real(DP) :: viscghb - real(DP) :: viscratio ! ------------------------------------------------------------------------------- ! ! -- Process density terms for each GHB @@ -737,7 +736,6 @@ function update_bnd_cond(bndvisc, viscref, spcfdcond) result(updatedcond) ! -- local real(DP) :: vscratio real(DP) :: updatedcond - integer(I4B) :: n ! ------------------------------------------------------------------------------- ! vscratio = calc_vsc_ratio(viscref, bndvisc) @@ -1042,11 +1040,9 @@ subroutine read_packagedata(this) ! -- dummy class(GwfVscType) :: this ! -- local - character(len=LINELENGTH) :: warnmsg, errmsg + character(len=LINELENGTH) :: errmsg character(len=LINELENGTH) :: line - character(len=LENMODELNAME) :: mname integer(I4B) :: ierr - integer(I4B) :: im integer(I4B) :: iviscspec logical :: isfound, endOfBlock logical :: blockrequired @@ -1303,7 +1299,6 @@ subroutine read_options(this) character(len=LINELENGTH) :: warnmsg, errmsg, keyword, keyword2 character(len=MAXCHARLEN) :: fname character(len=LINELENGTH) :: line - character(len=10) :: c10 character(len=16) :: c16 integer(I4B) :: ierr logical :: isfound, endOfBlock From 835699cd35dde6936576f89f63423bec64c0f709 Mon Sep 17 00:00:00 2001 From: Eric Morway Date: Thu, 27 Oct 2022 06:17:26 -0700 Subject: [PATCH 07/31] Addresses some of the review comments --- autotest/test_gwf_vsc01.py | 294 +++++++++++++------------ autotest/test_gwf_vsc02.py | 294 +++++++++++++------------ autotest/test_gwf_vsc03_sfr.py | 207 +++++++++-------- autotest/test_gwf_vsc04_lak.py | 229 ++++++++++--------- doc/ReleaseNotes/ReleaseNotes.tex | 4 +- src/Model/GroundWaterFlow/gwf3drn8.f90 | 4 +- src/Model/GroundWaterFlow/gwf3ghb8.f90 | 4 +- src/Model/GroundWaterFlow/gwf3riv8.f90 | 4 +- 8 files changed, 543 insertions(+), 497 deletions(-) diff --git a/autotest/test_gwf_vsc01.py b/autotest/test_gwf_vsc01.py index 70dee8eb098..e7d17f1f0fd 100644 --- a/autotest/test_gwf_vsc01.py +++ b/autotest/test_gwf_vsc01.py @@ -3,38 +3,35 @@ # Uses constant head and general-head boundaries on the left and right # sides of the model domain, respectively, to drive flow from left to # right. Tests that head-dependent boundary conditions are properly -# # accounting for viscosity when VSC is active. +# accounting for viscosity when VSC is active. # -# ### VSC Problem Setup - # Imports import os import sys -import matplotlib.pyplot as plt -import flopy -import numpy as np - -# Append to system path to include the common subdirectory - -sys.path.append(os.path.join("..", "common")) - -# Import common functionality - -import config -from figspecs import USGSFigure - -mf6exe = os.path.abspath(config.mf6_exe) - -# Scenario parameters - make sure there is at least one blank line before next item -hyd_cond = [1205.49396942506, 864.0] # Hydraulic conductivity ($m d^{-1}$) -parameters = { - "no-vsc01-bnd": {"vsc_on": False, "hydraulic_conductivity": hyd_cond[0]}, - "vsc01-bnd": {"vsc_on": True, "hydraulic_conductivity": hyd_cond[1]}, - "no-vsc01-k": {"vsc_on": False, "hydraulic_conductivity": hyd_cond[1]}, -} +import numpy as np +import pytest + +try: + import flopy +except: + msg = "Error. FloPy package is not available.\n" + msg += "Try installing using the following command:\n" + msg += " pip install flopy" + raise Exception(msg) + +from framework import testing_framework +from simulation import Simulation + +hyd_cond = [1205.49396942506, 864.0] # Hydraulic conductivity (m/d) +ex = ["no-vsc01-bnd", "vsc01-bnd", "no-vsc01-k"] +viscosity_on = [False, True, False] +hydraulic_conductivity = [hyd_cond[0], hyd_cond[1], hyd_cond[1]] +exdirs = [] +for s in ex: + exdirs.append(os.path.join("temp", s)) # Model units @@ -71,30 +68,34 @@ nouter, ninner = 100, 300 hclose, rclose, relax = 1e-10, 1e-6, 0.97 - -# ### Functions to build, write, run, and plot models # # MODFLOW 6 flopy GWF simulation object (sim) is returned # -def build_model(key, vsc_on, hydraulic_conductivity): - print("Building model...{}".format(key)) - +def build_model(idx, dir): # Base simulation and model name and workspace - ws = os.path.join("temp", "examples", key) + ws = dir + name = ex[idx] + + print("Building model...{}".format(name)) # generate names for each model - name = "vsc01" - gwfname = "gwf-" + key - gwtname = "gwt-" + key + gwfname = "gwf-" + name + gwtname = "gwt-" + name - sim = flopy.mf6.MFSimulation(sim_name=name, sim_ws=ws, exe_name=mf6exe) + sim = flopy.mf6.MFSimulation( + sim_name=name, version="mf6", exe_name="mf6", sim_ws=ws + ) + + # Instantiating time discretization tdis_ds = ((perlen, nstp, 1.0),) flopy.mf6.ModflowTdis( sim, nper=nper, perioddata=tdis_ds, time_units=time_units ) gwf = flopy.mf6.ModflowGwf(sim, modelname=gwfname, save_flows=True) + + # Instantiating solver ims = flopy.mf6.ModflowIms( sim, print_option="ALL", @@ -111,6 +112,8 @@ def build_model(key, vsc_on, hydraulic_conductivity): filename="{}.ims".format(gwfname), ) sim.register_ims_package(ims, [gwfname]) + + # Instantiating DIS flopy.mf6.ModflowGwfdis( gwf, length_units=length_units, @@ -122,15 +125,18 @@ def build_model(key, vsc_on, hydraulic_conductivity): top=top, botm=botm, ) + + # Instantiating NPF flopy.mf6.ModflowGwfnpf( gwf, save_specific_discharge=True, icelltype=0, - k=hydraulic_conductivity, + k=hydraulic_conductivity[idx], ) flopy.mf6.ModflowGwfic(gwf, strt=0.0) - if vsc_on: + # Instantiating VSC + if viscosity_on[idx]: # Instantiate viscosity (VSC) package vsc_filerecord = "{}.vsc.bin".format(gwfname) vsc_pd = [(0, 0.0, 20.0, gwtname, "temperature")] @@ -146,7 +152,7 @@ def build_model(key, vsc_on, hydraulic_conductivity): ) # Instantiating GHB - ghbcond = hydraulic_conductivity * delv * delc / (0.5 * delr) + ghbcond = hydraulic_conductivity[idx] * delv * delc / (0.5 * delr) ghbspd = [ [(0, i, ncol - 1), top, ghbcond, initial_temperature] for i in range(nrow) @@ -168,8 +174,8 @@ def build_model(key, vsc_on, hydraulic_conductivity): ) # Instatiating OC - head_filerecord = "{}.hds".format(key) - budget_filerecord = "{}.bud".format(key) + head_filerecord = "{}.hds".format(gwfname) + budget_filerecord = "{}.bud".format(gwfname) flopy.mf6.ModflowGwfoc( gwf, head_filerecord=head_filerecord, @@ -180,6 +186,8 @@ def build_model(key, vsc_on, hydraulic_conductivity): # Setup the GWT model for simulating heat transport # ------------------------------------------------- gwt = flopy.mf6.ModflowGwt(sim, modelname=gwtname) + + # Instantiating solver for GWT imsgwt = flopy.mf6.ModflowIms( sim, print_option="ALL", @@ -196,6 +204,8 @@ def build_model(key, vsc_on, hydraulic_conductivity): filename="{}.ims".format(gwtname), ) sim.register_ims_package(imsgwt, [gwtname]) + + # Instantiating DIS for GWT flopy.mf6.ModflowGwtdis( gwt, length_units=length_units, @@ -208,6 +218,7 @@ def build_model(key, vsc_on, hydraulic_conductivity): botm=botm, ) + # Instantiating MST for GWT flopy.mf6.ModflowGwtmst( gwt, porosity=porosity, @@ -218,18 +229,23 @@ def build_model(key, vsc_on, hydraulic_conductivity): filename="{}.mst".format(gwtname), ) + # Instantiating IC for GWT flopy.mf6.ModflowGwtic(gwt, strt=initial_temperature) + # Instantiating ADV for GWT flopy.mf6.ModflowGwtadv(gwt, scheme="UPSTREAM") + # Instantiating DSP for GWT flopy.mf6.ModflowGwtdsp(gwt, xt3d_off=True, diffc=D_m) + # Instantiating SSM for GWT sourcerecarray = [ ("CHD-1", "AUX", "TEMPERATURE"), ("GHB-1", "AUX", "TEMPERATURE"), ] flopy.mf6.ModflowGwtssm(gwt, sources=sourcerecarray) + # Instantiating OC for GWT flopy.mf6.ModflowGwtoc( gwt, concentration_filerecord="{}.ucn".format(gwtname), @@ -237,130 +253,118 @@ def build_model(key, vsc_on, hydraulic_conductivity): printrecord=[("CONCENTRATION", "LAST"), ("BUDGET", "LAST")], ) + # Instantiating GWF/GWT Exchange flopy.mf6.ModflowGwfgwt( sim, exgtype="GWF6-GWT6", exgmnamea=gwfname, exgmnameb=gwtname ) - return sim - + return sim, None -# Function to write and run model files -def write_and_run_model(sim, key, silent=True): - sim.write_simulation(silent=silent) - success, buff = sim.run_simulation(silent=False) - errmsg = f"simulation did not terminate successfully\n{buff}" - assert success, errmsg +def eval_results(sim): + print("evaluating results...") - # digest model budgets - simpath = sim.simulation_data.mfpath.get_sim_path() + # read flow results from model + name = ex[sim.idxsim] + gwfname = "gwf-" + name - modbud = key + ".bud" - fpth = os.path.join(simpath, modbud) - budobj = flopy.utils.CellBudgetFile(fpth, precision="double") + fname = gwfname + ".bud" + fname = os.path.join(sim.simpath, fname) + assert os.path.isfile(fname) + budobj = flopy.utils.CellBudgetFile(fname, precision="double") outbud = budobj.get_data(text=" GHB") - return outbud[-1] + if sim.idxsim == 0: + no_vsc_bud_last = np.array(outbud[-1].tolist()) + np.savetxt( + os.path.join(os.path.dirname(exdirs[sim.idxsim]), "mod1reslt.txt"), + no_vsc_bud_last, + ) + + elif sim.idxsim == 1: + with_vsc_bud_last = np.array(outbud[-1].tolist()) + np.savetxt( + os.path.join(os.path.dirname(exdirs[sim.idxsim]), "mod2reslt.txt"), + with_vsc_bud_last, + ) + + elif sim.idxsim == 2: + no_vsc_low_k_bud_last = np.array(outbud[-1].tolist()) + np.savetxt( + os.path.join(os.path.dirname(exdirs[sim.idxsim]), "mod3reslt.txt"), + no_vsc_low_k_bud_last, + ) + # if all 3 models have run, check relative results + if sim.idxsim == 2: + f1 = os.path.join(os.path.dirname(exdirs[sim.idxsim]), "mod1reslt.txt") + if os.path.isfile(f1): + no_vsc_bud_last = np.loadtxt(f1) + os.remove(f1) + + f2 = os.path.join(os.path.dirname(exdirs[sim.idxsim]), "mod2reslt.txt") + if os.path.isfile(f2): + with_vsc_bud_last = np.loadtxt(f2) + os.remove(f2) + + f3 = os.path.join(os.path.dirname(exdirs[sim.idxsim]), "mod3reslt.txt") + if os.path.isfile(f3): + no_vsc_low_k_bud_last = np.loadtxt(f3) + os.remove(f3) + + model1_exit = no_vsc_bud_last[:, 2].sum() + model2_exit = with_vsc_bud_last[:, 2].sum() + model3_exit = no_vsc_low_k_bud_last[:, 2].sum() + + # Ensure models 1 & 2 give nearly identical results + assert np.allclose(model1_exit, model2_exit, atol=1e-3), ( + "Flow in models " + + exdirs[0] + + " and " + + exdirs[1] + + " should be equal, but are not." + ) -def confirm_run_results( - modname1, modname2, modname3, no_vsc_bud, with_vsc_bud, low_k_bud -): + # Ensure the flow leaving model 3 is less than what leaves model 2 + assert abs(model2_exit) > abs(model3_exit), ( + "Exit flow from model " + + exdirs[1] + + " should be greater than flow existing " + + exdirs[2] + + ", but it is not." + ) - # Sum up total flow leaving the model through the GHB boundary - no_vsc_bud_last = np.array(no_vsc_bud.tolist()) - no_with_vsc_bud_last = np.array(with_vsc_bud.tolist()) - no_low_k_bud_last = np.array(low_k_bud.tolist()) - model1_exit = no_vsc_bud_last[:, 2].sum() - model2_exit = no_with_vsc_bud_last[:, 2].sum() - model3_exit = no_low_k_bud_last[:, 2].sum() +# - No need to change any code below +@pytest.mark.parametrize( + "idx, dir", + list(enumerate(exdirs)), +) +def test_mf6model(idx, dir): + # initialize testing framework + test = testing_framework() - # Ensure models 1 & 2 give nearly identical results - assert np.allclose(model1_exit, model2_exit, atol=1e-3), ( - "VSC results not right between models: " - + modname1 - + " and " - + modname2 - ) + # build the model + test.build_mf6_models(build_model, idx, dir) - # Ensure the flow leaving model 3 is less than that which leaves model 2 - assert abs(model2_exit) > abs(model3_exit), ( - "VSC results not right between models: " - + modname2 - + " and " - + modname3 - ) + # run the test model + test.run_mf6(Simulation(dir, exfunc=eval_results, idxsim=idx)) -def scenario(idx, silent=True): - # Three model runs that test model flows with and without - # viscosity active - - # Model Run 1 (Fake the effects of viscosity with pre-specified K) - # Model Run 2 (Account for the effects of viscosity) - # Model Run 3 (Ensure that base K scenario results in less flow) - # --------------------------------------------------------- - key = list(parameters.keys())[idx] - parameter_dict = parameters[key] - sim = build_model(key, **parameter_dict) - cbc_bud = write_and_run_model(sim, key, silent=silent) - - return key, cbc_bud - - -# nosetest - exclude block from this nosetest to the next nosetest -def test_01(): - # Compare model runs with and without viscosity package active - # Model 1 - no viscosity, but with elevated K's that mimic viscosity - # adjusted K values. - modname1, bnd_scen1_bud = scenario(0) - - # Model 2 - include viscosity - modname2, bnd_scen2_bud = scenario(1) - - # Model 3 - no viscosity with basecase K, flows should be reduced - modname3, bnd_scen3_bud = scenario(2) - - # Flow through model should be the same in models 1 & 2 based on - # specified K's. That is, the second model's adjusted K should - # be equal to the pre-specified K in model 1. The third model uses - # the base K of 864 m/d without simulating viscosity effects and - # should result in less flow through the model. - confirm_run_results( - modname1, - modname2, - modname3, - bnd_scen1_bud, - bnd_scen2_bud, - bnd_scen3_bud, - ) +def main(): + # initialize testing framework + test = testing_framework() + # run the test model + for idx, dir in enumerate(exdirs): + test.build_mf6_models(build_model, idx, dir) + sim = Simulation(dir, exfunc=eval_results, idxsim=idx) + test.run_mf6(sim) -# nosetest end if __name__ == "__main__": - # Compare model runs with and without viscosity package active - # Model 1 - no viscosity, but with elevated K's that mimic viscosity - # adjusted K values. - modname1, bnd_scen1_bud = scenario(0) - - # Model 2 - include viscosity - modname2, bnd_scen2_bud = scenario(1) - - # Model 3 - no viscosity with basecase K, flows should be reduced - modname3, bnd_scen3_bud = scenario(2) - - # Flow through model should be the same in models 1 & 2 based on - # specified K's. That is, the second model's adjusted K should - # be equal to the pre-specified K in model 1. The third model uses - # the base K of 864 m/d without simulating viscosity effects and - # should result in less flow through the model. - confirm_run_results( - modname1, - modname2, - modname3, - bnd_scen1_bud, - bnd_scen2_bud, - bnd_scen3_bud, - ) + # print message + print(f"standalone run of {os.path.basename(__file__)}") + + # run main routine + main() diff --git a/autotest/test_gwf_vsc02.py b/autotest/test_gwf_vsc02.py index 0da0c106257..316b1ad877b 100644 --- a/autotest/test_gwf_vsc02.py +++ b/autotest/test_gwf_vsc02.py @@ -3,37 +3,38 @@ # Uses general-head and drain boundaries on the left and right # sides of the model domain, respectively, to drive flow from left to # right. Tests that head-dependent boundary conditions are properly -# accounting for viscosity when VSC is active. -# - -# ### VSC Problem Setup +# accounting for viscosity when VSC is active. Similar to gwf-vsc01-bnd +# but employs head-dependent boundary on the left and right side of the +# model # Imports import os import sys -import matplotlib.pyplot as plt -import flopy -import numpy as np -# Append to system path to include the common subdirectory +import numpy as np +import pytest -sys.path.append(os.path.join("..", "common")) +try: + import flopy +except: + msg = "Error. FloPy package is not available.\n" + msg += "Try installing using the following command:\n" + msg += " pip install flopy" + raise Exception(msg) # Import common functionality - -import config -from figspecs import USGSFigure - -mf6exe = os.path.abspath(config.mf6_exe) +from framework import testing_framework +from simulation import Simulation # Setup scenario input -hyd_cond = [1205.49396942506, 864.0] # Hydraulic conductivity ($m d^{-1}$) -parameters = { - "no-vsc02-bnd": {"vsc_on": False, "hydraulic_conductivity": hyd_cond[0]}, - "vsc02-bnd": {"vsc_on": True, "hydraulic_conductivity": hyd_cond[1]}, - "no-vsc02-k": {"vsc_on": False, "hydraulic_conductivity": hyd_cond[1]}, -} +hyd_cond = [1205.49396942506, 864.0] # Hydraulic conductivity (m/d) +ex = ["no-vsc02-bnd", "vsc02-bnd", "no-vsc02-k"] +viscosity_on = [False, True, False] +hydraulic_conductivity = [hyd_cond[0], hyd_cond[1], hyd_cond[1]] +exdirs = [] +for s in ex: + exdirs.append(os.path.join("temp", s)) # Model units @@ -70,30 +71,34 @@ nouter, ninner = 100, 300 hclose, rclose, relax = 1e-10, 1e-6, 0.97 - -# ### Functions to build, write, run, and plot models # # MODFLOW 6 flopy GWF simulation object (sim) is returned # -def build_model(key, vsc_on, hydraulic_conductivity): - print("Building model...{}".format(key)) - +def build_model(idx, dir): # Base simulation and model name and workspace - ws = os.path.join("temp", "examples", key) + ws = dir + name = ex[idx] + + print("Building model...{}".format(name)) # generate names for each model - name = "vsc02" - gwfname = "gwf-" + key - gwtname = "gwt-" + key + gwfname = "gwf-" + name + gwtname = "gwt-" + name + + sim = flopy.mf6.MFSimulation( + sim_name=name, sim_ws=ws, exe_name="mf6", version="mf6" + ) - sim = flopy.mf6.MFSimulation(sim_name=name, sim_ws=ws, exe_name=mf6exe) + # Instantiating time discretization tdis_ds = ((perlen, nstp, 1.0),) flopy.mf6.ModflowTdis( sim, nper=nper, perioddata=tdis_ds, time_units=time_units ) gwf = flopy.mf6.ModflowGwf(sim, modelname=gwfname, save_flows=True) + + # Instantiating solver ims = flopy.mf6.ModflowIms( sim, print_option="ALL", @@ -110,6 +115,8 @@ def build_model(key, vsc_on, hydraulic_conductivity): filename="{}.ims".format(gwfname), ) sim.register_ims_package(ims, [gwfname]) + + # Instantiating DIS flopy.mf6.ModflowGwfdis( gwf, length_units=length_units, @@ -121,15 +128,18 @@ def build_model(key, vsc_on, hydraulic_conductivity): top=top, botm=botm, ) + + # Instantiating NPF flopy.mf6.ModflowGwfnpf( gwf, save_specific_discharge=True, icelltype=0, - k=hydraulic_conductivity, + k=hydraulic_conductivity[idx], ) flopy.mf6.ModflowGwfic(gwf, strt=0.0) - if vsc_on: + # Instantiating VSC + if viscosity_on[idx]: # Instantiate viscosity (VSC) package vsc_filerecord = "{}.vsc.bin".format(gwfname) vsc_pd = [(0, 0.0, 20.0, gwtname, "temperature")] @@ -145,7 +155,7 @@ def build_model(key, vsc_on, hydraulic_conductivity): ) # Instantiating GHB - ghbcond = hydraulic_conductivity * delv * delc / (0.5 * delr) + ghbcond = hydraulic_conductivity[idx] * delv * delc / (0.5 * delr) ghbspd = [ [(0, i, 0), top + 3, ghbcond, initial_temperature] for i in range(nrow) ] @@ -169,8 +179,8 @@ def build_model(key, vsc_on, hydraulic_conductivity): ) # Instatiatingi OC - head_filerecord = "{}.hds".format(key) - budget_filerecord = "{}.bud".format(key) + head_filerecord = "{}.hds".format(gwfname) + budget_filerecord = "{}.bud".format(gwfname) flopy.mf6.ModflowGwfoc( gwf, head_filerecord=head_filerecord, @@ -179,7 +189,10 @@ def build_model(key, vsc_on, hydraulic_conductivity): ) # Setup the GWT model for simulating heat transport + # ------------------------------------------------- gwt = flopy.mf6.ModflowGwt(sim, modelname=gwtname) + + # Instantiating solver for GWT imsgwt = flopy.mf6.ModflowIms( sim, print_option="ALL", @@ -196,6 +209,8 @@ def build_model(key, vsc_on, hydraulic_conductivity): filename="{}.ims".format(gwtname), ) sim.register_ims_package(imsgwt, [gwtname]) + + # Instantiating DIS for GWT flopy.mf6.ModflowGwtdis( gwt, length_units=length_units, @@ -208,6 +223,7 @@ def build_model(key, vsc_on, hydraulic_conductivity): botm=botm, ) + # Instantiating MST for GWT flopy.mf6.ModflowGwtmst( gwt, porosity=porosity, @@ -218,151 +234,141 @@ def build_model(key, vsc_on, hydraulic_conductivity): filename="{}.mst".format(gwtname), ) + # Instantiating IC for GWT flopy.mf6.ModflowGwtic(gwt, strt=initial_temperature) + # Instantiating ADV for GWT flopy.mf6.ModflowGwtadv(gwt, scheme="UPSTREAM") + # Instantiating DSP for GWT flopy.mf6.ModflowGwtdsp(gwt, xt3d_off=True, diffc=D_m) + # Instantiating SSM for GWT sourcerecarray = [ ("GHB-1", "AUX", "TEMPERATURE"), ("DRN-1", "AUX", "TEMPERATURE"), ] flopy.mf6.ModflowGwtssm(gwt, sources=sourcerecarray) + # Instantiating OC for GWT flopy.mf6.ModflowGwtoc( gwt, concentration_filerecord="{}.ucn".format(gwtname), saverecord=[("CONCENTRATION", "ALL")], printrecord=[("CONCENTRATION", "LAST"), ("BUDGET", "LAST")], ) + + # Instantiating GWF/GWT Exchange flopy.mf6.ModflowGwfgwt( sim, exgtype="GWF6-GWT6", exgmnamea=gwfname, exgmnameb=gwtname ) - return sim + return sim, None -# Function to write model files -def write_and_run_model(sim, key, silent=True): - sim.write_simulation(silent=silent) +def eval_results(sim): + print("evaluating results...") - success, buff = sim.run_simulation(silent=False) - errmsg = f"simulation did not terminate successfully\n{buff}" - assert success, errmsg + # read flow results from model + name = ex[sim.idxsim] + gwfname = "gwf-" + name - # digest model budgets - simpath = sim.simulation_data.mfpath.get_sim_path() + fname = gwfname + ".bud" + fname = os.path.join(sim.simpath, fname) + assert os.path.isfile(fname) + budobj = flopy.utils.CellBudgetFile(fname, precision="double") + outbud = budobj.get_data(text=" GHB") - modbud = key + ".bud" - fpth = os.path.join(simpath, modbud) - budobj = flopy.utils.CellBudgetFile(fpth, precision="double") - outbud = budobj.get_data(text=" DRN") + if sim.idxsim == 0: + no_vsc_bud_last = np.array(outbud[-1].tolist()) + np.savetxt( + os.path.join(os.path.dirname(exdirs[sim.idxsim]), "mod1reslt.txt"), + no_vsc_bud_last, + ) - return outbud[-1] + elif sim.idxsim == 1: + with_vsc_bud_last = np.array(outbud[-1].tolist()) + np.savetxt( + os.path.join(os.path.dirname(exdirs[sim.idxsim]), "mod2reslt.txt"), + with_vsc_bud_last, + ) + + elif sim.idxsim == 2: + no_vsc_low_k_bud_last = np.array(outbud[-1].tolist()) + np.savetxt( + os.path.join(os.path.dirname(exdirs[sim.idxsim]), "mod3reslt.txt"), + no_vsc_low_k_bud_last, + ) + # if all 3 models have run, check relative results + if sim.idxsim == 2: + f1 = os.path.join(os.path.dirname(exdirs[sim.idxsim]), "mod1reslt.txt") + if os.path.isfile(f1): + no_vsc_bud_last = np.loadtxt(f1) + os.remove(f1) + + f2 = os.path.join(os.path.dirname(exdirs[sim.idxsim]), "mod2reslt.txt") + if os.path.isfile(f2): + with_vsc_bud_last = np.loadtxt(f2) + os.remove(f2) + + f3 = os.path.join(os.path.dirname(exdirs[sim.idxsim]), "mod3reslt.txt") + if os.path.isfile(f3): + no_vsc_low_k_bud_last = np.loadtxt(f3) + os.remove(f3) + + model1_exit = no_vsc_bud_last[:, 2].sum() + model2_exit = with_vsc_bud_last[:, 2].sum() + model3_exit = no_vsc_low_k_bud_last[:, 2].sum() + + # Ensure models 1 & 2 give nearly identical results + assert np.allclose(model1_exit, model2_exit, atol=1e-3), ( + "Flow in models " + + exdirs[0] + + " and " + + exdirs[1] + + " should be equal, but are not." + ) -def confirm_run_results( - modname1, modname2, modname3, no_vsc_bud, with_vsc_bud, low_k_bud -): + # Ensure the flow leaving model 3 is less than what leaves model 2 + assert abs(model2_exit) > abs(model3_exit), ( + "Exit flow from model " + + exdirs[1] + + " should be greater than flow existing " + + exdirs[2] + + ", but it is not." + ) - # Sum up total flow leaving the model through the GHB boundary - no_vsc_bud_last = np.array(no_vsc_bud.tolist()) - no_with_vsc_bud_last = np.array(with_vsc_bud.tolist()) - no_low_k_bud_last = np.array(low_k_bud.tolist()) - model1_exit = no_vsc_bud_last[:, 2].sum() - model2_exit = no_with_vsc_bud_last[:, 2].sum() - model3_exit = no_low_k_bud_last[:, 2].sum() +# - No need to change any code below +@pytest.mark.parametrize( + "idx, dir", + list(enumerate(exdirs)), +) +def test_mf6model(idx, dir): + # initialize testing framework + test = testing_framework() - # Ensure models 1 & 2 give nearly identical results - assert np.allclose(model1_exit, model2_exit, atol=1e-3), ( - "VSC results not right between models: " - + modname1 - + " and " - + modname2 - ) + # build the model + test.build_mf6_models(build_model, idx, dir) - # Ensure the flow leaving model 3 is less than that which leaves model 2 - assert abs(model2_exit) > abs(model3_exit), ( - "VSC results not right between models: " - + modname2 - + " and " - + modname3 - ) + # run the test model + test.run_mf6(Simulation(dir, exfunc=eval_results, idxsim=idx)) -def scenario(idx, silent=True): - # Three model runs to check relative flows w/ and w/o VSC - - # Model Run 1 - no viscosity, but uses a back-of-the-envelope - # calculated viscosity that gives same result as - # model run 2 - # Model Run 2 - includes viscosity, should give same results as - # model run 1 after applying viscosity ratio to - # prescribed K's - # Model Run 3 - no viscosity, uses same K as scenario 2 and should - # result in reduced flow relative to model run 2 - # --------------------------------------------------------- - key = list(parameters.keys())[idx] - parameter_dict = parameters[key] - sim = build_model(key, **parameter_dict) - bud_obj = write_and_run_model(sim, key, silent=silent) - - return key, bud_obj - - -# nosetest - exclude block from this nosetest to the next nosetest -def test_01(): - # Compare model runs with and without viscosity package active - # Model 1 - no viscosity, but with elevated K's that mimic viscosity - # adjusted K values. - modname1, bnd_scen1_bud = scenario(0) - - # Model 2 - include viscosity - modname2, bnd_scen2_bud = scenario(1) - - # Model 3 - no viscosity with basecase K, flows should be reduced - modname3, bnd_scen3_bud = scenario(2) - - # Flow through model should be the same in models 1 & 2 based on - # specified K's. That is, the second model's adjusted K should - # be equal to the pre-specified K in model 1. The third model uses - # the base K of 864 m/d without simulating viscosity effects and - # should result in less flow through the model. - confirm_run_results( - modname1, - modname2, - modname3, - bnd_scen1_bud, - bnd_scen2_bud, - bnd_scen3_bud, - ) +def main(): + # initialize testing framework + test = testing_framework() + # run the test model + for idx, dir in enumerate(exdirs): + test.build_mf6_models(build_model, idx, dir) + sim = Simulation(dir, exfunc=eval_results, idxsim=idx) + test.run_mf6(sim) -# nosetest end if __name__ == "__main__": - # Compare model runs with and without viscosity package active - # Model 1 - no viscosity, but with elevated K's that mimic viscosity - # adjusted K values. - modname1, bnd_scen1_bud = scenario(0) - - # Model 2 - include viscosity - modname2, bnd_scen2_bud = scenario(1) - - # Model 3 - no viscosity with basecase K, flows should be reduced - modname3, bnd_scen3_bud = scenario(2) - - # Flow through model should be the same in models 1 & 2 based on - # specified K's. That is, the second model's adjusted K should - # be equal to the pre-specified K in model 1. The third model uses - # the base K of 864 m/d without simulating viscosity effects and - # should result in less flow through the model. - confirm_run_results( - modname1, - modname2, - modname3, - bnd_scen1_bud, - bnd_scen2_bud, - bnd_scen3_bud, - ) + # print message + print(f"standalone run of {os.path.basename(__file__)}") + + # run main routine + main() diff --git a/autotest/test_gwf_vsc03_sfr.py b/autotest/test_gwf_vsc03_sfr.py index 8f258b2ff15..2bd580a32e5 100644 --- a/autotest/test_gwf_vsc03_sfr.py +++ b/autotest/test_gwf_vsc03_sfr.py @@ -1,37 +1,36 @@ # Scenario envisioned by this test is a river running through a V-shaped # valley that loses water to the aquifer at the upper end until it goes -# dry, then begins to gain flow again at the lower reaches. River water +# dry, then begins to gain flow again in the lower reaches. River water # enters the simulation at 8 deg C. Aquifer water starts out at 35 deg C. # Reference viscosity temperature is 20 deg C. With the VSC package active, # the simulation should predict less loss of river water to the aquifer # and more discharge of gw to the stream, compared to the same simulation # with the VSC package inactive. -import sys -import math -from io import StringIO -import os -import shutil -import numpy as np -from subprocess import check_output -import flopy +# Imports -# Append to system path to include the common subdirectory - -sys.path.append(os.path.join("..", "common")) +import os +import sys -# Import common functionality +import numpy as np +import pytest -import config -from figspecs import USGSFigure +try: + import flopy +except: + msg = "Error. FloPy package is not available.\n" + msg += "Try installing using the following command:\n" + msg += " pip install flopy" + raise Exception(msg) -mf6exe = os.path.abspath(config.mf6_exe) +from framework import testing_framework +from simulation import Simulation -# Setup scenario input -parameters = { - "no-vsc-sfr01": {"viscosity_on": False}, - "vsc-sfr01": {"viscosity_on": True}, -} +ex = ["no-vsc-sfr01", "vsc-sfr01"] +viscosity_on = [False, True] +exdirs = [] +for s in ex: + exdirs.append(os.path.join("temp", s)) # Equation for determining land surface elevation with a stream running down the middle def topElev_sfrCentered(x, y): @@ -97,22 +96,26 @@ def topElev_sfrCentered(x, y): rhob = (1 - porosity) * rho_solids # Bulk density ($kg/m^3$) K_d = C_s / (rho_water * C_p_w) # Partitioning coefficient ($m^3/kg$) - +# # MODFLOW 6 flopy GWF & GWT simulation object (sim) is returned # -def build_model(key, viscosity_on=False): - print("Building model...{}".format(key)) +def build_model(idx, dir): # Base simulation and model name and workspace - ws = os.path.join("temp", "examples", key) + ws = dir + name = ex[idx] + + print("Building model...{}".format(name)) # generate names for each model - name = "vsc03" - gwfname = "gwf-" + key - gwtname = "gwt-" + key + gwfname = "gwf-" + name + gwtname = "gwt-" + name - sim = flopy.mf6.MFSimulation(sim_name=name, sim_ws=ws, exe_name=mf6exe) + sim = flopy.mf6.MFSimulation( + sim_name=name, sim_ws=ws, exe_name="mf6", version="mf6" + ) + # Instantiating time discretization tdis_rc = [] for i in range(len(nstp)): tdis_rc.append((perlen[i], nstp[i], tsmult[i])) @@ -125,6 +128,7 @@ def build_model(key, viscosity_on=False): sim, modelname=gwfname, save_flows=True, newtonoptions="newton" ) + # Instantiating solver ims = flopy.mf6.ModflowIms( sim, print_option="ALL", @@ -178,7 +182,7 @@ def build_model(key, viscosity_on=False): flopy.mf6.ModflowGwfic(gwf, strt=strthd) # Instantiate viscosity package - if viscosity_on: + if viscosity_on[idx]: vsc_filerecord = "{}.vsc.bin".format(gwfname) vsc_pd = [(0, 0.0, 20.0, gwtname, "TEMPERATURE")] flopy.mf6.ModflowGwfvsc( @@ -330,7 +334,10 @@ def build_model(key, viscosity_on=False): ) # Setup the GWT model for simulating heat transport + # ------------------------------------------------- gwt = flopy.mf6.ModflowGwt(sim, modelname=gwtname) + + # Instantiating solver for GWT imsgwt = flopy.mf6.ModflowIms( sim, print_option="ALL", @@ -347,6 +354,8 @@ def build_model(key, viscosity_on=False): filename="{}.ims".format(gwtname), ) sim.register_ims_package(imsgwt, [gwtname]) + + # Instantiating DIS for GWT flopy.mf6.ModflowGwtdis( gwt, length_units=length_units, @@ -429,84 +438,92 @@ def build_model(key, viscosity_on=False): filename="{}.gwfgwt".format(gwtname), ) - return sim - - -# Function to write model files -def write_and_run_model(sim, key, silent=True): - sim.write_simulation(silent=silent) - - success, buff = sim.run_simulation(silent=False) - errmsg = f"simulation did not terminate successfully\n{buff}" - assert success, errmsg - - # slurp in sfr cell-by-cell budgets - simpath = sim.simulation_data.mfpath.get_sim_path() - - sfrbud = "gwf-" + key + ".sfr.cbc" - fpth = os.path.join(simpath, sfrbud) - budobj = flopy.utils.CellBudgetFile(fpth, precision="double") - bud = budobj.get_data(text=" GWF") + return sim, None - return bud[-1] +def eval_results(sim): + print("evaluating results...") -def scenario(idx, silent=True): - # Two model runs to check relative flows w/ and w/o VSC + # read flow results from model + name = ex[sim.idxsim] + gwfname = "gwf-" + name - # Model Run 1 (Do not account for the effects of viscosity) - # Model Run 2 (Account for the effects of viscosity) - # --------------------------------------------------------- - key = list(parameters.keys())[idx] - parameter_dict = parameters[key] - sim = build_model(key, **parameter_dict) - sfr_rbr_bud = write_and_run_model(sim, key, silent=silent) - - return sfr_rbr_bud + fname = gwfname + ".sfr.cbc" + fname = os.path.join(sim.simpath, fname) + assert os.path.isfile(fname) + budobj = flopy.utils.CellBudgetFile(fname, precision="double") + outbud = budobj.get_data(text=" GWF") + if sim.idxsim == 0: + no_vsc_bud_last = np.array(outbud[-1].tolist()) + np.savetxt( + os.path.join(os.path.dirname(exdirs[sim.idxsim]), "mod1reslt.txt"), + no_vsc_bud_last, + ) -def confirm_run_results(no_vsc_bud, with_vsc_bud): - # sum up total losses and total gains in the first 10 reaches - # and the last 10 reaches - for i in np.arange(10): - # upper reaches - assert abs(no_vsc_bud[i][2]) > abs( - with_vsc_bud[i][2] - ), "GW/SW not as expected" + elif sim.idxsim == 1: + with_vsc_bud_last = np.array(outbud[-1].tolist()) + np.savetxt( + os.path.join(os.path.dirname(exdirs[sim.idxsim]), "mod2reslt.txt"), + with_vsc_bud_last, + ) - # lower reaches - assert abs(no_vsc_bud[-(i + 1)][2]) < abs( - with_vsc_bud[-(i + 1)][2] - ), "GW/SW not as expected" + # if both models have run, check relative results + if sim.idxsim == 1: + f1 = os.path.join(os.path.dirname(exdirs[sim.idxsim]), "mod1reslt.txt") + if os.path.isfile(f1): + no_vsc_bud_last = np.loadtxt(f1) + os.remove(f1) + + f2 = os.path.join(os.path.dirname(exdirs[sim.idxsim]), "mod2reslt.txt") + if os.path.isfile(f2): + with_vsc_bud_last = np.loadtxt(f2) + os.remove(f2) + + # sum up total losses and total gains in the first 10 reaches + # and the last 10 reaches + for i in np.arange(10): + # upper reaches + assert abs(no_vsc_bud_last[i, 2]) > abs( + with_vsc_bud_last[i, 2] + ), "GW/SW not as expected in upper reaches of viscosity w/ sfr example" + + # lower reaches + assert abs(no_vsc_bud_last[-(i + 1), 2]) < abs( + with_vsc_bud_last[-(i + 1), 2] + ), "GW/SW not as expected in lower reaches of viscosity w/ sfr example" + + +# - No need to change any code below +@pytest.mark.parametrize( + "idx, dir", + list(enumerate(exdirs)), +) +def test_mf6model(idx, dir): + # initialize testing framework + test = testing_framework() + # build the model + test.build_mf6_models(build_model, idx, dir) -def test_01(): - # Compare model runs with and without viscosity package active - # Model 1 - no viscosity - sfr_scen1_bud = scenario(0, silent=False) + # run the test model + test.run_mf6(Simulation(dir, exfunc=eval_results, idxsim=idx)) - # Model 2 - include viscosity - sfr_scen2_bud = scenario(1, silent=False) - # Seepage from upper SFR reaches should decrease owing to cold - # stream water. GW discharge back to lower SFR reaches should - # increase owing to hot groundwater - confirm_changes(sfr_scen1_bud, sfr_scen2_bud) +def main(): + # initialize testing framework + test = testing_framework() + # run the test model + for idx, dir in enumerate(exdirs): + test.build_mf6_models(build_model, idx, dir) + sim = Simulation(dir, exfunc=eval_results, idxsim=idx) + test.run_mf6(sim) -# nosetest end if __name__ == "__main__": - # ### V-shaped, linear river valley model - - # Compare model runs with and without viscosity package active - # Model 1 - no viscosity - sfr_scen1_bud = scenario(0) - - # Model 2 - include viscosity - sfr_scen2_bud = scenario(1) + # print message + print(f"standalone run of {os.path.basename(__file__)}") - # Seepage from upper SFR reaches should decrease owing to cold - # stream water. GW discharge back to lower SFR reaches should - # increase owing to hot groundwater - confirm_run_results(sfr_scen1_bud, sfr_scen2_bud) + # run main routine + main() diff --git a/autotest/test_gwf_vsc04_lak.py b/autotest/test_gwf_vsc04_lak.py index 82a8f7276ea..318c7385305 100644 --- a/autotest/test_gwf_vsc04_lak.py +++ b/autotest/test_gwf_vsc04_lak.py @@ -28,15 +28,13 @@ raise Exception(msg) from framework import testing_framework -import config +from simulation import Simulation -mf6exe = os.path.abspath(config.mf6_exe) - -# Setup scenario input -parameters = { - "no-vsc04-lak": {"viscosity_on": False}, - "vsc04-lak": {"viscosity_on": True}, -} +ex = ["no-vsc04-lak", "vsc04-lak"] +viscosity_on = [False, True] +exdirs = [] +for s in ex: + exdirs.append(os.path.join("temp", s)) # Model units length_units = "m" @@ -171,21 +169,27 @@ # Viscosity related parameters tviscref = 20.0 +# # MODFLOW 6 flopy GWF & GWT simulation object (sim) is returned # -def build_model(key, viscosity_on=False): + + +def build_model(idx, dir): global lak_lkup_dict - print("Building model...{}".format(key)) # Base simulation and model name and workspace - ws = os.path.join("temp", "examples", key) + ws = dir + name = ex[idx] + + print("Building model...{}".format(name)) # generate names for each model - name = "vsc04" - gwfname = "gwf-" + key - gwtname = "gwt-" + key + gwfname = "gwf-" + name + gwtname = "gwt-" + name - sim = flopy.mf6.MFSimulation(sim_name=name, sim_ws=ws, exe_name=mf6exe) + sim = flopy.mf6.MFSimulation( + sim_name=name, sim_ws=ws, exe_name="mf6", version="mf6" + ) tdis_rc = [] for i in range(len(nstp)): @@ -199,6 +203,7 @@ def build_model(key, viscosity_on=False): sim, modelname=gwfname, save_flows=True, newtonoptions="newton" ) + # Instantiating solver ims = flopy.mf6.ModflowIms( sim, print_option="ALL", @@ -254,7 +259,7 @@ def build_model(key, viscosity_on=False): flopy.mf6.ModflowGwfic(gwf, strt=strthd) # Instantiate viscosity package - if viscosity_on: + if viscosity_on[idx]: vsc_filerecord = "{}.vsc.bin".format(gwfname) vsc_pd = [(0, 0.0, tviscref, gwtname, "TEMPERATURE")] flopy.mf6.ModflowGwfvsc( @@ -466,7 +471,7 @@ def build_model(key, viscosity_on=False): filename="{}.lak".format(gwfname), ) - # pull in th etabfile defining the lake stage, vol, & surface area + # pull in the tabfile defining the lake stage, vol, & surface area fname = os.path.join("data", "vsc04-laktab", "stg-vol-surfarea.csv") tabinput = [] with open(fname, "r") as f: @@ -544,6 +549,8 @@ def build_model(key, viscosity_on=False): scheme = "TVD" else: raise Exception() + + # Instantiate advection package flopy.mf6.ModflowGwtadv( gwt, scheme=scheme, filename="{}.adv".format(gwtname) ) @@ -605,7 +612,7 @@ def build_model(key, viscosity_on=False): filename="{}.lkt".format(gwtname), ) - # GWF GWT exchange + # GWF-GWT exchange flopy.mf6.ModflowGwfgwt( sim, exgtype="GWF6-GWT6", @@ -614,109 +621,121 @@ def build_model(key, viscosity_on=False): filename=f"{name}.gwfgwt", ) - return sim - - -def write_and_run_model(sim, key, silent=True): - sim.write_simulation(silent=silent) - - success, buff = sim.run_simulation(silent=False) - errmsg = f"simulation did not terminate successfully\n{buff}" - assert success, errmsg - - # slurp in sfr cell-by-cell budgets - simpath = sim.simulation_data.mfpath.get_sim_path() + return sim, None - lakbud = "gwf-" + key + ".lak.bud" - fpth = os.path.join(simpath, lakbud) - budobj = flopy.utils.CellBudgetFile(fpth, precision="double") - bud = budobj.get_data(text=" GWF") - return bud[-1] +def eval_results(sim): + print("evaluating results...") + # read flow results from model + name = ex[sim.idxsim] + gwfname = "gwf-" + name -def scenario(idx, silent=True): - # Two model runs to check relative flows w/ and w/o VSC + fname = gwfname + ".lak.bud" + fname = os.path.join(sim.simpath, fname) + assert os.path.isfile(fname) + budobj = flopy.utils.CellBudgetFile(fname, precision="double") + outbud = budobj.get_data(text=" GWF") - # Model Run 1 (Do not account for the effects of viscosity) - # Model Run 2 (Account for the effects of viscosity) - # --------------------------------------------------------- - key = list(parameters.keys())[idx] - parameter_dict = parameters[key] - sim = build_model(key, **parameter_dict) - lak_cbc_bud = write_and_run_model(sim, key, silent=silent) - - return lak_cbc_bud - - -def confirm_run_results(no_vsc_bud, with_vsc_bud): - # talley some flows on the left and right sides of the lake for comparison - # test - no_vsc_bud_np = np.array(no_vsc_bud.tolist()) - with_vsc_bud_np = np.array(with_vsc_bud.tolist()) - - left_chk_no_vsc = [] - right_chk_no_vsc = [] - left_chk_with_vsc = [] - right_chk_with_vsc = [] + if sim.idxsim == 0: + no_vsc_bud_last = np.array(outbud[-1].tolist()) + np.savetxt( + os.path.join(os.path.dirname(exdirs[sim.idxsim]), "mod1reslt.txt"), + no_vsc_bud_last, + ) - for idx in np.arange(no_vsc_bud_np.shape[0]): - k, i, j = lak_lkup_dict[idx] + elif sim.idxsim == 1: + with_vsc_bud_last = np.array(outbud[-1].tolist()) + np.savetxt( + os.path.join(os.path.dirname(exdirs[sim.idxsim]), "mod2reslt.txt"), + with_vsc_bud_last, + ) - # left side of lake - if j < 7: - if no_vsc_bud_np[idx, 2] > 0 and with_vsc_bud_np[idx, 2] > 0: - left_chk_no_vsc.append(no_vsc_bud_np[idx, 2]) - left_chk_with_vsc.append(with_vsc_bud_np[idx, 2]) + # if both models have run, check relative results + if sim.idxsim == 1: + f1 = os.path.join(os.path.dirname(exdirs[0]), "mod1reslt.txt") + if os.path.isfile(f1): + no_vsc_bud_last = np.loadtxt(f1) + os.remove(f1) + + f2 = os.path.join(os.path.dirname(exdirs[1]), "mod2reslt.txt") + if os.path.isfile(f2): + with_vsc_bud_last = np.loadtxt(f2) + os.remove(f2) + + # talley some flows on the left and right sides of the lake for comparison + # test + no_vsc_bud_np = np.array(no_vsc_bud_last.tolist()) + with_vsc_bud_np = np.array(with_vsc_bud_last.tolist()) + + left_chk_no_vsc = [] + right_chk_no_vsc = [] + left_chk_with_vsc = [] + right_chk_with_vsc = [] + + for idx in np.arange(no_vsc_bud_np.shape[0]): + k, i, j = lak_lkup_dict[idx] + + # left side of lake + if j < 7: + if no_vsc_bud_np[idx, 2] > 0 and with_vsc_bud_np[idx, 2] > 0: + left_chk_no_vsc.append(no_vsc_bud_np[idx, 2]) + left_chk_with_vsc.append(with_vsc_bud_np[idx, 2]) + + # right side of lake + if j > 9: + if no_vsc_bud_np[idx, 2] < 0 and with_vsc_bud_np[idx, 2] < 0: + right_chk_no_vsc.append(no_vsc_bud_np[idx, 2]) + right_chk_with_vsc.append(with_vsc_bud_np[idx, 2]) + + # Check that all the flows entering the lak in the 'with vsc' model are greater + # than their 'no vsc' counterpart + assert np.greater( + np.array(left_chk_with_vsc), np.array(left_chk_no_vsc) + ).all(), ( + "Lake inflow did no increase with VSC turned on and should have." + ) - # right side of lake - if j > 9: - if no_vsc_bud_np[idx, 2] < 0 and with_vsc_bud_np[idx, 2] < 0: - right_chk_no_vsc.append(no_vsc_bud_np[idx, 2]) - right_chk_with_vsc.append(with_vsc_bud_np[idx, 2]) + # Check that all the flows leaving the lak in the 'with vsc' model are less + # than their 'no vsc' counterpart (keep in mind values are negative, which + # affects how the comparison is made) + assert np.greater( + np.array(right_chk_with_vsc), np.array(right_chk_no_vsc) + ).all(), ( + "Lake outflow did no decrease with VSC turned on and should have." + ) - # Check that all the flows entering the lak in the 'with vsc' model are greater - # than their 'no vsc' counterpart - assert np.greater( - np.array(left_chk_with_vsc), np.array(left_chk_no_vsc) - ).all(), "Lake inflow did no increase with VSC turned on and should have." - # Check that all the flows leaving the lak in the 'with vsc' model are less - # than their 'no vsc' counterpart (keep in mind values are negative, which - # affects how the comparison is made) - assert np.greater( - np.array(right_chk_with_vsc), np.array(right_chk_no_vsc) - ).all(), "Lake outflow did no decrease with VSC turned on and should have." +# - No need to change any code below +@pytest.mark.parametrize( + "idx, dir", + list(enumerate(exdirs)), +) +def test_mf6model(idx, dir): + # initialize testing framework + test = testing_framework() + # build the model + test.build_mf6_models(build_model, idx, dir) -# nosetest - exclude block from this nosetest to the next nosetest -def test_01(): - # Compare model runs with and without viscosity package active - # Model 1 - no viscosity - lak_scen1_bud = scenario(0, silent=False) + # run the test model + test.run_mf6(Simulation(dir, exfunc=eval_results, idxsim=idx)) - # Model 2 - include viscosity - lak_scen2_bud = scenario(1, silent=False) - # GW discharge into left side of lake should increase owing to hot - # groundwater. GW discharge out of the right side of the lake should - # increase owing to cold lake water. - confirm_run_results(lak_scen1_bud, lak_scen2_bud) +def main(): + # initialize testing framework + test = testing_framework() + # run the test model + for idx, dir in enumerate(exdirs): + test.build_mf6_models(build_model, idx, dir) + sim = Simulation(dir, exfunc=eval_results, idxsim=idx) + test.run_mf6(sim) -# nosetest end if __name__ == "__main__": - # ### Adapted from Merritt & Konikow (2000) example problem 1. - - # Compare model runs with and without viscosity package active - # Model 1 - no viscosity - lak_scen1_bud = scenario(0) - - # Model 2 - include viscosity - lak_scen2_bud = scenario(1) + # print message + print(f"standalone run of {os.path.basename(__file__)}") - # GW discharge into left side of lake should increase owing to hot - # groundwater. GW discharge out of the right side of the lake should - # increase owing to cold lake water. - confirm_run_results(lak_scen1_bud, lak_scen2_bud) + # run main routine + main() diff --git a/doc/ReleaseNotes/ReleaseNotes.tex b/doc/ReleaseNotes/ReleaseNotes.tex index 871409faa3b..5aefe2555b4 100644 --- a/doc/ReleaseNotes/ReleaseNotes.tex +++ b/doc/ReleaseNotes/ReleaseNotes.tex @@ -192,14 +192,14 @@ \section{Changes Introduced in this Release} \underline{NEW FUNCTIONALITY} \begin{itemize} - \item A new Viscosity (VSC) package for the Groundwater Flow (GWF) Model is introduced in this release. The effects of viscosity are accounted for by updates intercell conductance, as well as the conductance between the aquifer and head-dependent boundaries, based on simulated concentrations and\/or temperatures. As with other packages, the viscosity package is activated by specifying ``VSC6'' as the file type in a GWF name file. Testing of the VSC Package is accomplished using example test problems; however, changes to the code and input may be required in response to user needs and testing. + \item A new Viscosity (VSC) package for the Groundwater Flow (GWF) Model is introduced in this release. The effects of viscosity are accounted for by updates intercell conductance, as well as the conductance between the aquifer and head-dependent boundaries, based on simulated concentrations and\/or temperatures. The viscosity package is activated by specifying ``VSC6'' as the file type in a GWF name file. Testing of the VSC Package is accomplished using new autotests; however, changes to the code and input may be required in response to user needs and testing. % \item xxx % \item xxx \end{itemize} \underline{EXAMPLES} \begin{itemize} - \item Four new example problems added to the ``autotest'' folder that tests the functionality of the new Viscosity (VSC) Package and named test\_gwf\_vsc01, test\_gwf\_vsc02, test\_gwf\_vsc03\_sfr, and test\_gwf\_vsc04\_lak + % \item xxx % \item xxx % \item xxx \end{itemize} diff --git a/src/Model/GroundWaterFlow/gwf3drn8.f90 b/src/Model/GroundWaterFlow/gwf3drn8.f90 index 665e2e5a0dd..6fc8d3765ae 100644 --- a/src/Model/GroundWaterFlow/gwf3drn8.f90 +++ b/src/Model/GroundWaterFlow/gwf3drn8.f90 @@ -3,7 +3,8 @@ module DrnModule use ConstantsModule, only: DZERO, DONE, DTWO, & LENFTYPE, LENPACKAGENAME, LENAUXNAME, LINELENGTH, & LENMEMPATH, LENVARNAME, LENMEMSEPARATOR - use MemoryHelperModule, only: create_mem_path, split_mem_address + use MemoryHelperModule, only: create_mem_path, split_mem_address, & + memPathSeparator use SmoothingModule, only: sQSaturation, sQSaturationDerivative, & sQuadraticSaturation use BndModule, only: BndType @@ -20,7 +21,6 @@ module DrnModule ! character(len=LENFTYPE) :: ftype = 'DRN' character(len=LENPACKAGENAME) :: text = ' DRN' - character(len=LENMEMSEPARATOR), parameter :: memPathSeparator = '/' ! type, extends(BndType) :: DrnType diff --git a/src/Model/GroundWaterFlow/gwf3ghb8.f90 b/src/Model/GroundWaterFlow/gwf3ghb8.f90 index 0d94e072ad9..3c2eb9309db 100644 --- a/src/Model/GroundWaterFlow/gwf3ghb8.f90 +++ b/src/Model/GroundWaterFlow/gwf3ghb8.f90 @@ -2,7 +2,8 @@ module ghbmodule use KindModule, only: DP, I4B, LGP use ConstantsModule, only: DZERO, LENFTYPE, LENPACKAGENAME, LENMEMPATH, & LENVARNAME, LENMEMSEPARATOR - use MemoryHelperModule, only: create_mem_path, split_mem_address + use MemoryHelperModule, only: create_mem_path, split_mem_address, & + memPathSeparator use BndModule, only: BndType use ObsModule, only: DefaultObsIdProcessor use TimeSeriesLinkModule, only: TimeSeriesLinkType, & @@ -16,7 +17,6 @@ module ghbmodule ! character(len=LENFTYPE) :: ftype = 'GHB' character(len=LENPACKAGENAME) :: text = ' GHB' - character(len=LENMEMSEPARATOR), parameter :: memPathSeparator = '/' ! type, extends(BndType) :: GhbType diff --git a/src/Model/GroundWaterFlow/gwf3riv8.f90 b/src/Model/GroundWaterFlow/gwf3riv8.f90 index 31357fb05b4..7dd24f4eadb 100644 --- a/src/Model/GroundWaterFlow/gwf3riv8.f90 +++ b/src/Model/GroundWaterFlow/gwf3riv8.f90 @@ -2,7 +2,8 @@ module rivmodule use KindModule, only: DP, I4B, LGP use ConstantsModule, only: DZERO, LENFTYPE, LENPACKAGENAME, LENMEMPATH, & LENVARNAME, LENMEMSEPARATOR - use MemoryHelperModule, only: create_mem_path, split_mem_address + use MemoryHelperModule, only: create_mem_path, split_mem_address, & + memPathSeparator use BndModule, only: BndType use ObsModule, only: DefaultObsIdProcessor use TimeSeriesLinkModule, only: TimeSeriesLinkType, & @@ -16,7 +17,6 @@ module rivmodule ! character(len=LENFTYPE) :: ftype = 'RIV' character(len=LENPACKAGENAME) :: text = ' RIV' - character(len=LENMEMSEPARATOR), parameter :: memPathSeparator = '/' ! type, extends(BndType) :: RivType contains From 95903baff14713d0f2786d0eca983aaba39b5520 Mon Sep 17 00:00:00 2001 From: Eric Morway Date: Thu, 27 Oct 2022 13:55:09 -0700 Subject: [PATCH 08/31] Switching up how head-dependent boundary packages, specifically DRN, GHB, and RIV set the ivsc flag --- src/Model/GroundWaterFlow/gwf3drn8.f90 | 7 --- src/Model/GroundWaterFlow/gwf3ghb8.f90 | 7 --- src/Model/GroundWaterFlow/gwf3riv8.f90 | 7 --- src/Model/GroundWaterFlow/gwf3vsc8.f90 | 30 ++++++++++-- src/Model/ModelUtilities/BoundaryPackage.f90 | 50 ++++++++++++++++---- 5 files changed, 67 insertions(+), 34 deletions(-) diff --git a/src/Model/GroundWaterFlow/gwf3drn8.f90 b/src/Model/GroundWaterFlow/gwf3drn8.f90 index 6fc8d3765ae..b823b53af84 100644 --- a/src/Model/GroundWaterFlow/gwf3drn8.f90 +++ b/src/Model/GroundWaterFlow/gwf3drn8.f90 @@ -96,13 +96,6 @@ subroutine drn_create(packobj, id, ibcnum, inunit, iout, namemodel, pakname) packobj%iscloc = 2 !sfac applies to conductance packobj%ictMemPath = create_mem_path(namemodel, 'NPF') ! - ! -- check if vsc package exists and set flag if so - vscpath = trim(namemodel)//memPathSeparator//'VSC' - call split_mem_address(vscpath, locmempath, locvarname, vscexists) - if (vscexists) then - packobj%ivsc = 1 - end if - ! ! -- return return end subroutine drn_create diff --git a/src/Model/GroundWaterFlow/gwf3ghb8.f90 b/src/Model/GroundWaterFlow/gwf3ghb8.f90 index 3c2eb9309db..202fa79011d 100644 --- a/src/Model/GroundWaterFlow/gwf3ghb8.f90 +++ b/src/Model/GroundWaterFlow/gwf3ghb8.f90 @@ -82,13 +82,6 @@ subroutine ghb_create(packobj, id, ibcnum, inunit, iout, namemodel, pakname) packobj%iscloc = 2 packobj%ictMemPath = create_mem_path(namemodel, 'NPF') ! - ! -- check if vsc package exists and set flag if so - vscpath = trim(namemodel)//memPathSeparator//'VSC' - call split_mem_address(vscpath, locmempath, locvarname, vscexists) - if (vscexists) then - packobj%ivsc = 1 - end if - ! ! -- return return end subroutine ghb_create diff --git a/src/Model/GroundWaterFlow/gwf3riv8.f90 b/src/Model/GroundWaterFlow/gwf3riv8.f90 index 7dd24f4eadb..30bb828df93 100644 --- a/src/Model/GroundWaterFlow/gwf3riv8.f90 +++ b/src/Model/GroundWaterFlow/gwf3riv8.f90 @@ -81,13 +81,6 @@ subroutine riv_create(packobj, id, ibcnum, inunit, iout, namemodel, pakname) packobj%iscloc = 2 !sfac applies to conductance packobj%ictMemPath = create_mem_path(namemodel, 'NPF') ! - ! -- check if vsc package exists and set flag if so - vscpath = trim(namemodel)//memPathSeparator//'VSC' - call split_mem_address(vscpath, locmempath, locvarname, vscexists) - if (vscexists) then - packobj%ivsc = 1 - end if - ! ! -- return return end subroutine riv_create diff --git a/src/Model/GroundWaterFlow/gwf3vsc8.f90 b/src/Model/GroundWaterFlow/gwf3vsc8.f90 index e7051233a76..c564c9d6477 100644 --- a/src/Model/GroundWaterFlow/gwf3vsc8.f90 +++ b/src/Model/GroundWaterFlow/gwf3vsc8.f90 @@ -29,7 +29,7 @@ module GwfVscModule type, extends(NumericalPackageType) :: GwfVscType integer(I4B), pointer :: thermivisc => null() !< viscosity formulation flag (1:Linear, 2:Nonlinear) integer(I4B), pointer :: idxtmpr => null() !< if greater than 0 then an index for identifying whether the "species" array is temperature - integer(I4B), pointer :: ioutvisc => null() !< unit number for saving viscosity + integer(I4B), pointer :: ioutvisc => null() !< unit number for saving viscosity integer(I4B), pointer :: iconcset => null() !< if 1 then conc points to a gwt (or gwe) model%x array integer(I4B), pointer :: ireadelev => null() !< if 1 then elev has been allocated and filled integer(I4B), dimension(:), pointer, contiguous :: ivisc => null() !< viscosity formulation flag for each species (1:Linear, 2:Nonlinear) @@ -39,7 +39,7 @@ module GwfVscModule integer(I4B), dimension(:), pointer :: ibound => null() !< store pointer to ibound integer(I4B), pointer :: nviscspecies => null() !< number of concentration species used in viscosity equation - real(DP), dimension(:), pointer, contiguous :: dviscdc => null() !< linear change in viscosity with change in concentration + real(DP), dimension(:), pointer, contiguous :: dviscdc => null() !< linear change in viscosity with change in concentration real(DP), dimension(:), pointer, contiguous :: cviscref => null() !< reference concentration used in viscosity equation real(DP), dimension(:), pointer, contiguous :: ctemp => null() !< temporary array of size (nviscspec) to pass to calc_visc_x character(len=LENMODELNAME), dimension(:), allocatable :: cmodelname !< names of gwt (or gwe) models used in viscosity equation @@ -122,7 +122,7 @@ function calc_visc(ivisc, viscref, dviscdc, cviscref, conc, & do i = 1, nviscspec if (ivisc(i) == 1) then - visc = visc + dviscdc(i) * (conc(i) - cviscref(i)) + visc = visc + dviscdc(i) * (conc(i) - cviscref(i)) else expon = -1 * a3 * ((conc(i) - cviscref(i)) / & ((conc(i) + a4) * (cviscref(i) + a4))) @@ -269,6 +269,9 @@ subroutine vsc_ar_bnd(this, packobj) ! ---------------------------------------------------------------------------- ! -- modules use BndModule, only: BndType + use DrnModule, only: DrnType + use GhbModule, only: GhbType + use RivModule, only: RivType use LakModule, only: LakType use SfrModule, only: SfrType use MawModule, only: MawType @@ -280,6 +283,27 @@ subroutine vsc_ar_bnd(this, packobj) ! ! -- Add density terms based on boundary package type select case (packobj%filtyp) + case ('DRN') + ! + ! -- activate viscosity for the drain package + select type (packobj) + type is (DrnType) + call packobj%bnd_activate_viscosity() + end select + case ('GHB') + ! + ! -- activate viscosity for the drain package + select type (packobj) + type is (GhbType) + call packobj%bnd_activate_viscosity() + end select + case ('RIV') + ! + ! -- activate viscosity for the drain package + select type (packobj) + type is (RivType) + call packobj%bnd_activate_viscosity() + end select case ('LAK') ! ! -- activate viscosity for lake package diff --git a/src/Model/ModelUtilities/BoundaryPackage.f90 b/src/Model/ModelUtilities/BoundaryPackage.f90 index f9a83dbb649..e602b2436ed 100644 --- a/src/Model/ModelUtilities/BoundaryPackage.f90 +++ b/src/Model/ModelUtilities/BoundaryPackage.f90 @@ -156,6 +156,9 @@ module BndModule ! -- procedure to support time series procedure, public :: bnd_rp_ts ! + ! -- procedure to inform package that viscosity active + procedure, public :: bnd_activate_viscosity + ! ! -- procedure to backup user-specified conductance procedure, private :: bnd_store_user_cond ! @@ -1111,16 +1114,9 @@ subroutine allocate_arrays(this, nodelist, auxvar) call mem_allocate(this%bound, this%ncolbnd, this%maxbound, 'BOUND', & this%memoryPath) ! - !-- Allocate array for storing user-specified conductances if vsc active - if (this%ivsc == 1) then - call mem_allocate(this%condinput, this%maxbound, 'CONDINPUT', & - this%memoryPath) - do i = 1, this%maxbound - this%condinput(i) = DZERO - end do - else - call mem_allocate(this%condinput, 0, 'CONDINPUT', this%memoryPath) - end if + !-- Allocate array for storing user-specified conductances + ! Will be reallocated to size maxbound if vsc active + call mem_allocate(this%condinput, 0, 'CONDINPUT', this%memoryPath) ! ! -- Allocate hcof and rhs call mem_allocate(this%hcof, this%maxbound, 'HCOF', this%memoryPath) @@ -2074,4 +2070,38 @@ subroutine save_print_model_flows(icbcfl, ibudfl, icbcun, iprflow, & return end subroutine save_print_model_flows + !> @brief Activate viscosity terms + !! + !! Method to activate addition of viscosity terms when package type + !! is DRN, GHB, or RIV (method not needed by other packages at this point) + !! + !< + subroutine bnd_activate_viscosity(this) + ! -- modules + use MemoryManagerModule, only: mem_reallocate + ! -- dummy variables + class(BndType), intent(inout) :: this !< BndType object + ! -- local variables + integer(I4B) :: i + ! + ! -- Set ivsc and reallocate viscratios to be of size MAXBOUND + this%ivsc = 1 + ! + ! -- Allocate array for storing user-specified conductances + ! modified by updated viscosity values + call mem_reallocate(this%condinput, this%maxbound, 'CONDINPUT', & + this%memoryPath) + do i = 1, this%maxbound + this%condinput(i) = DZERO + end do + ! + ! -- Notify user via listing file viscosity accounted for by standard + ! boundary package. + write (this%iout, '(/1x,a,a)') 'VISCOSITY ACTIVE IN ', & + trim(this%filtyp)//' PACKAGE CALCULATIONS: '//trim(adjustl(this%packName)) + ! + ! -- return + return + end subroutine bnd_activate_viscosity + end module BndModule From 06ad7fd0b282edc9989f78b8a437f933eaf6580f Mon Sep 17 00:00:00 2001 From: Eric Morway Date: Thu, 27 Oct 2022 14:13:48 -0700 Subject: [PATCH 09/31] forgot to remove some unused variables with last edits --- src/Model/GroundWaterFlow/gwf3drn8.f90 | 9 ++------- src/Model/GroundWaterFlow/gwf3ghb8.f90 | 7 +------ src/Model/GroundWaterFlow/gwf3riv8.f90 | 7 +------ 3 files changed, 4 insertions(+), 19 deletions(-) diff --git a/src/Model/GroundWaterFlow/gwf3drn8.f90 b/src/Model/GroundWaterFlow/gwf3drn8.f90 index b823b53af84..65013261dec 100644 --- a/src/Model/GroundWaterFlow/gwf3drn8.f90 +++ b/src/Model/GroundWaterFlow/gwf3drn8.f90 @@ -1,8 +1,7 @@ module DrnModule use KindModule, only: DP, I4B, LGP - use ConstantsModule, only: DZERO, DONE, DTWO, & - LENFTYPE, LENPACKAGENAME, LENAUXNAME, LINELENGTH, & - LENMEMPATH, LENVARNAME, LENMEMSEPARATOR + use ConstantsModule, only: DZERO, DONE, DTWO, LENFTYPE, LENPACKAGENAME, & + LENAUXNAME, LINELENGTH, LENMEMSEPARATOR use MemoryHelperModule, only: create_mem_path, split_mem_address, & memPathSeparator use SmoothingModule, only: sQSaturation, sQSaturationDerivative, & @@ -67,10 +66,6 @@ subroutine drn_create(packobj, id, ibcnum, inunit, iout, namemodel, pakname) character(len=*), intent(in) :: pakname ! -- local type(DrnType), pointer :: drnobj - character(len=LENMEMPATH) :: vscpath !< if vsc exist, this is path name - character(len=LENMEMPATH) :: locmempath !< the memory path for the model - character(len=LENVARNAME) :: locvarname !< the package name to check on - logical(LGP) :: vscexists !< flag will be true if vsc is active ! ------------------------------------------------------------------------------ ! ! -- allocate the object and assign values to object variables diff --git a/src/Model/GroundWaterFlow/gwf3ghb8.f90 b/src/Model/GroundWaterFlow/gwf3ghb8.f90 index 202fa79011d..d81b45c1a7e 100644 --- a/src/Model/GroundWaterFlow/gwf3ghb8.f90 +++ b/src/Model/GroundWaterFlow/gwf3ghb8.f90 @@ -1,7 +1,6 @@ module ghbmodule use KindModule, only: DP, I4B, LGP - use ConstantsModule, only: DZERO, LENFTYPE, LENPACKAGENAME, LENMEMPATH, & - LENVARNAME, LENMEMSEPARATOR + use ConstantsModule, only: DZERO, LENFTYPE, LENPACKAGENAME, LENMEMSEPARATOR use MemoryHelperModule, only: create_mem_path, split_mem_address, & memPathSeparator use BndModule, only: BndType @@ -54,10 +53,6 @@ subroutine ghb_create(packobj, id, ibcnum, inunit, iout, namemodel, pakname) character(len=*), intent(in) :: pakname ! -- local type(GhbType), pointer :: ghbobj - character(len=LENMEMPATH) :: vscpath !< if vsc exist, this is path name - character(len=LENMEMPATH) :: locmempath !< the memory path for the model - character(len=LENVARNAME) :: locvarname !< the package name to check on - logical(LGP) :: vscexists !< flag will be true if vsc is active ! ------------------------------------------------------------------------------ ! ! -- allocate the object and assign values to object variables diff --git a/src/Model/GroundWaterFlow/gwf3riv8.f90 b/src/Model/GroundWaterFlow/gwf3riv8.f90 index 30bb828df93..825eeb8dac0 100644 --- a/src/Model/GroundWaterFlow/gwf3riv8.f90 +++ b/src/Model/GroundWaterFlow/gwf3riv8.f90 @@ -1,7 +1,6 @@ module rivmodule use KindModule, only: DP, I4B, LGP - use ConstantsModule, only: DZERO, LENFTYPE, LENPACKAGENAME, LENMEMPATH, & - LENVARNAME, LENMEMSEPARATOR + use ConstantsModule, only: DZERO, LENFTYPE, LENPACKAGENAME, LENMEMSEPARATOR use MemoryHelperModule, only: create_mem_path, split_mem_address, & memPathSeparator use BndModule, only: BndType @@ -53,10 +52,6 @@ subroutine riv_create(packobj, id, ibcnum, inunit, iout, namemodel, pakname) character(len=*), intent(in) :: pakname ! -- local type(RivType), pointer :: rivobj - character(len=LENMEMPATH) :: vscpath !< if vsc exist, this is path name - character(len=LENMEMPATH) :: locmempath !< the memory path for the model - character(len=LENVARNAME) :: locvarname !< the package name to check on - logical(LGP) :: vscexists !< flag will be true if vsc is active ! ------------------------------------------------------------------------------ ! ! -- allocate the object and assign values to object variables From dce208109607e3e85ad0432bc95f5dacbbb2e49b Mon Sep 17 00:00:00 2001 From: Eric Morway Date: Fri, 28 Oct 2022 05:01:31 -0700 Subject: [PATCH 10/31] Viscosity chapter for Supplemental Technical Information document --- doc/SuppTechInfo/Figures/VSCnonlinear.pdf | Bin 0 -> 16210 bytes doc/SuppTechInfo/body.tex | 6 + doc/SuppTechInfo/mf6suptechinfo.bbl | 51 +++- doc/SuppTechInfo/python/VSC-nonlinear.ipynb | 291 ++++++++++++++++++++ doc/SuppTechInfo/viscosity.tex | 74 +++++ 5 files changed, 421 insertions(+), 1 deletion(-) create mode 100644 doc/SuppTechInfo/Figures/VSCnonlinear.pdf create mode 100644 doc/SuppTechInfo/python/VSC-nonlinear.ipynb create mode 100644 doc/SuppTechInfo/viscosity.tex diff --git a/doc/SuppTechInfo/Figures/VSCnonlinear.pdf b/doc/SuppTechInfo/Figures/VSCnonlinear.pdf new file mode 100644 index 0000000000000000000000000000000000000000..96346ab714c8ef7657bd7c9865eeb2fd296d0231 GIT binary patch literal 16210 zcmeHuc|4U*^gl_sl$}I%?J3;-a&6g{>`Njl`@UY+lA>fwA#0W_Az8}4l#(S&b}c9= zAthN#6s>;qT=^vVe!hK|*X#Gs_j-+ao_Xe(Ip@rI=A1e69zi{2RWXz}7AAQ88T{HU z7z&PryIVQIWMtq7JxhCAFE|>M7{C!m$31M}2qjCBrHi{gTuu&V>t+L0lw9+IiXTbM zkYq`+g=1GdX^-TEzu2*gKl96J)*m{p)Cn+2KrG}1?}7Vk>CgoSJ0>8 z&s^zet^q#+M;JO-+Bmw|!|^Mxo9J3O*;j0-+}zwrUZ4x`z7inC z)(zYdRvJ(vx_f)TAvTo^;Rt10A4h9j12xb!xHBSJx_Nn65^deAkFRpRQXh1L(6+U4 zv{ZEW1MNbcVbFMSEF6u+ic3QM=s~ZMR;6ZO>*elEv<6~?moDEu>PxCQ11aZ2}H-elkzF;S`D zlqtspHip~eR9K&cDRX67w6k|M(k;FZ-(~ksR9g4?l9kQr#!uVoyym~}2zkFd@MO_~ z{HIbxesA^lwmd_>AKp^361{;!NCt{s~F6cAUq}5j<8I3BBi#9IrqTIv(c+>7z z=M)xe#%}beT%y_6qdGQMI5480%_G{9x}ATkds@Vx>0U}rn(S?Isx9N5-_$*&PuF@1 zZKJ+Gy-%&2TJ8Xc%%;+Ph;OeO4a!ijP42c;HK^6ZsWHIb@eN5n>DjdF?U(H-zAj$F zbtR>81HvxE*UeeQA~D{#6qVe?-&q_u5$9_3r20t!vG}WS*?2jksOw18$tr)QLEraB zLeFal`RVk18Yww`hw)=!bn<6&O99^43IEivW7jXf)ag=?^0lFV8(L zDO^A{?FtV}o-yojluR029JwlIamnD5SG+6Z?4nJkp?g{6ID5PCA`Xuel`ZoH*`rp5h231W7)&aQXv+iM(-o=iJBc0+J@Hd_3tti*j; zLUhNYX9-vd;?lT{)@&#hbu5x%&sH9K|IqAd`G?98MaU5 zmSG$))Yn+oAjY!9vv&^^gnC#GUD}b(GC8;R1OmzQF&(#Ip+X1^!X*0FSsY!oq`L5ABv7CetzN z9vr;BD96wpYJ4X*(1iL*HS6q9d&#MjH$N}<<+`?fel$^3AOYb9?DSpzT@xd^Vd;Y*}fyWs4E=>CIOxsUx0#KWRIhlH@&99M&o~sJF8r6W@Q< zqVwJB;nL474n>Dzr;wHBa`?n;ZJu7!9vsfwcjC&Py)yX}s^94xsjSa%G+z--E#@-F zxzaUUsuaijqz zYd%|EtU>2fP+@>NP1OEp%BQK5?(3z6-07{FR1t+ewdl{5q|*xuQH*BcHDn8?X|Xl7 zznoEVzD=VgzCz9(yEVbFEF?G&W{-8n-rT&s>CC1r8Dq%-$tT83c}Cyw*;!}@JA9-u zph+Vbuwjw8M2E>2r*8&|XUU;3KPYc{pxFf)Ra*8W`l zF5(Z3!|yBgza`s)G@8FY_OH))MX*Lot0wnlf^2WSj%p8P)FKe!|POLyR)7WFOZ z%L`ZTu}3in*PMw7Jp38PVJ^_2sc&P<<}Pk|CU$E`E!D2;_ueQC@0EI#l_v9<^6>2! z)g=$EzY`i}5b}=6R_c61@qzvGp~9e+!Oc-xhv}MX9ukYa84M;UzZ`3yu3YwZ4$AgB zxxLFyOpw-$>&Sg`s|!AuC3@17jqJejm>XyMFPUdQonQ35Pq=Mva!S0Oe@o!p$ejaR z+D^psr^`=aA9;got`(jVmqQMQ&di)-(yPeWDd)t7pEwS>QQ$gN8qP9`soso%@kzMPHqmLZ zmw84^+&-DwzWnxa!c6cvCJn6rXjio**C%#s(@aO%@b4ccE>=xXOvd7G1njT6X>5j8d@e z#eEmIm_E44{^s3f0{i)}R)G>7dbT|k%K65X-M)EYUR288Ci{{fI-Ja>mQ$Yca6Vvs zL1OmZa$~#%3(MX8?zy)GjRNTPxrlUq1Ce}t?(}phUp7d^X)6$ATdHp)9NsK!b2zSy zqoSoQotdd*TZ>Qjy&uB5xA))F;cDK1yubDJFqq%J1KBu&k06RxW&SX=CKKlR#NuSz z@Ts>nd!?CqK$79q!<6eG1~#uIBChuysD<49dR+8`W8PbJsu#IOOBAX zWW}rMx9aQU_+);XWxx6Qaa@hke6V^-OVVEVrW#yT}p9Z>SI$Q&>Gi6<6S|Kx%_ktV!8_rO3^}Lns>78Gu znLQl*{Leu_uU$0!o6ca+xZjl~uEtXd#iTTUtOY;9HQSQ2>p0Kg_q@Q%)ACej$Inp6 zNk0p#84Az)xP8FuN#6dXt3$np(nU^B7Yzfh`%%Q4h*x1@8R*TPp>;~qR*i!$@e4xvvz^&&^ga0*^7?ziC(wwjFJ_;3rv!Gk zjv42oY(C4g9t#nAE~3;uN#jiGId>&^D{_Q$d#dMhrmb_#ovmt5997v#I`fed!$F<2 z=>)wjJC!a$k!Q+<+Y8jhl1oN5w|+PhJAE%U6En$ro&s|wmx-zW&8gkbhK}zejw?GI zfXUS!jh^M~cyOtp)9v-moUa#;q{bSYZu*h=bn5dL>SWaM+Z&9=+BK-+%0f#Fg#-pf z41+_#fl)!iQAj*_we_!qg~Y%RhTc{r^2$}!o#?u{mRnssu3bpU8fX?bBWZ^w*y`j3 z;V0v)YcWQt{as_vPt9u$Nfa6xvN8zmtk{2CD0nibY`I$5 zWV?1ScbU+$&?+%DtKKVR^|22#ZPo4$3(-pHeWZC+Xf1FYQ>5^0O2ebs;$DkldcMnH z`dr$VSGXHZX|LEnd+~MC1vzCYHXe+XuXjSF@uP@-YrAcj!Wm=)IKmaIhH-F&7qsSHiB0^|3baU9btHO`pq>C(2#A*dOp!np zWm_+6qN4}Nod`#fgBG9}P^@i9B07>+`{GC>q?_RHpDWP2mgUc$&^SPlj-@N$@lU?j zs3Jrz;Ae&E6*(Ppv>`bFfvf~u00XP86hz0WSQWgiq0j;W4cJ$*^ia2Tw09su{6U06 zPsGp|JUMb;55gSy73%+j@Bf%&4G`^dv{rDlcd>=~(YEv>6AHwG1Gk1GTUTSK{1+!{ zM2Il?hrl)bKnz0sn8EQ#Ltc2`J&va5Mo20|}1D;o&$OXatXeOJG4=;Jx6%M?ub^=NOQH-z@>&huS1a5U6OG1*Ia1sP^PLKfff^s|x4H#VM z7Yq8up@Cos(0f=o4&;(RSWr$*z-U6kfP?_$Q2*pXg9Hd@0D^@HR=?cN|0&pnO#gR)=df2h_q= z2_d(Dl|X?PR>l$1gMZqGYC$}al?f;mbpKB)5K%}ZjQs3p0|Yz)e4Q%^QiK&9fb`(^ zM3yzs3rHu{B(gq0Is7thwA*3AiaP_70QqNGuc7rV0E$slRi1rOP=>v zqP)=5|1*pVW`A-V7cu~3?~NQ4{W)o(|8}W~1`=881K{+fiPR|CsQ}m!IOer`rah+Q z!0Zdv(s(;dN;kX&b%@FS~xn+hW&d!p0qkhG|%DF?z%yHpxk-p4fjZqwM#gA9&$%nBbb9{ypd z^11ESrB?$Ymkt-0Ekw#jboNXYh|SRFM2}sCKOD6VUxXiPPHRySe%Kv|c~u&R-JD1L zo*$L)c5oDTx4AcRv+m%d$$*5g$8rw0>UtPxW0K5i3>U6>H;wdzHfNSL|2+}XrVYSvE5iY~3L z)1uafh5{EUWAJEP@FOO!%ak5lluzxT5Yc7R*?^9}UAtkBzY`)GCmq*>WKcSheulFx zaw&d%-bOfa@yCoDy50D+Mg+wkgm+5$RT;RW5Z&^F(NQbuSH4d&y1N>xlo70*BE4Dr znxCnBE>DTNRW8+1*n~_sTgXp4AEq6jIjr&BQ&q&o#eil;32Z)%ca-MctZT9S$+A?Bn#=}Br)Nf->|_Wi5#FR{kq`pCWi>Dpz=zmbB*{=R5b(Yqa`gkqSIrRJoy_%3=P z!&g0Vs9yGfsz)gA;r(%kS(48-Xfl)~xR0r+D#+5;bB2y#ntM4z6?#Ih$fteeVoLD~ zd#dX8#>bd#*yHM>luzbQ#?xqonR;9k<{}kuX&AA48fAKF%@vmSMm-M7 zt#YY&6g$lu6{4vjD3lavxOcfy#BA@Irz)(a>f=H2+SpOi{Yr6GU6&K|7m-!b3!de% zYTozT2h#{*DTA|lGg>DZO@vbPUx}nopyh-M=FN2iW-E3+7|+f<_%-eOaO}kod{skP z{ga0T^;4qy^I~qwEJtR}{h*4zY)W=#e~%G@nfKQ?|C{t6uVU>S4c2LHV2&Q@6}c&& z`(B%vQ7}CF{FK?HE~7#if~p#oki<|Rxc_b3S&UZTN8*t&{SS9soa|ro&Whu&E}0OI z-3og`e2{8YrpwcgXsa|b$ufSPqcj(~hn0~+Y$S+!w2o(ajx)dUacL;A>4eQDivxXb z@~uh_xYSJ*m@U7?vcI{aSK>1IZV&uW-}TTelGWL*7b!&Qu5FD-&L6xS9+DVWxbxv$ z^#JDH=8uF~gX)V4o4_Z)37{{S@m zuDjpPXr{?Uvf1$xwiXURofm&Ife7nB`xly~(-Gt2HNZ+Vr*UYhsqdUqL52BXjk_sReDNd2awZZ4GcBS%f1B%A}9(dE%zwN3FK8F`<<|7nS&c`$8 z4UT+rS8At?xeL!ih~x=!d}`HX7^N{|tQ(I#sB~)Ae$+HZRJMJ*Z~yC>b|bbr_QFfa zdYo3H-yJsBa*z;w`#NY|T(D*E>RBAgVM?+P!dhH+*AU|Duf^EhjmU~_Qn+Na-B+7U zw3xjc(>7x)Q7%;hckvTs9c!gf-aN<|(`}IxS(X^nLfn-Pi=#el@pzJ3|NNPK2?KPb z?1AQ6T_e{fKKV!}AwG=c-|`D!n9?l0`zH5<=*eQm-CYY>(!Lt}civxRWwv9aHb_a2 zO_Y+g3b-K;A}afqzP9(yf2U{PW4?C-`K?_*|C{`Pd%N~~(2i1IX@JvGy{qNZ)l;Kowz|2 zTt4R+{7~cTCr^KhYv291JZF5!!)LMqg=^ijf1?nDX4X=OLEivIq7+NX62<|%1iLL{ zFQKj=qDE1iQ=Or?gR+9*bfsY0nX_h!ScQZ$CQP|@LUv3Vl2m+2TPWHq*A zU`@W37?oJfI4U@;@?ypr{?p&aWl~ZP5|?MN$y#`yr2Ow82l%hDh)%E_M}BYn&}~ya zS;ccLJ!;cAKDMs$y$dEm{pliOT~*3eAIs3hXOlaF$F@!srx}&s)Nhq4lwy~>I??f^ zaQ53x(T+O)a5S~c0AU;6<5jg~Bx_E$v2XZS<(Rzqf}Am*7?-8s_}03Pe)gL>_^)pT zx6_;n%%ao!pncgpO#bC-5q~1xr#a8t^YW!PYU!@H(2ln;MQ&5Lty6r;bL!jhvQ$(0 z#8ZYt(Y|xiTF$p$B2ew;YEG-nvh~f&au!W7?0narVoiJ$pfq7#+WB1JYVWt*Q;Upx z=j_gIAjiM;{7_)U^P3!FG+CP93`&b>ifZrGww%l$-8%5DzKB=5;&fh_{1#u~%>`A| ztPiLWu9~;#<;>`MTK(SO-Eb`9o4rpPUwP?}U=UE#;+RO9bHID;qc39JX}8TkA&2WS zlN9^#O!heO>TJ(vHII+SKB|??rgb+r*u2^nV)s#G_T;6V`bVDIoO)>OHsE}U{ZnCS z<@MZGAwxY!Fqb#a7Vyk`r!Mf43fX|uzx6G_^8YtRBX2|d0?N?-Q=pJt=AOigQ{3Nw*8bk<7D~4 zT~mfu-VxIua(8t;HU+E4l>oaU}TfX0>PVK+9bLnO9hTZsEtGyDv!V za@&QrHb=co7w59bfkNnIW3J;`t&sF0%Z#ooMtdP;$)BZhwp4ZGG$ z{I{tHwa(bc>eg(fTJgOk8~jDh=FMZ?mY?G8P&J_Clb?&VRl5f>x7rjRO<+Md?q71; z5oEFjr?wDs&eCMWEp8KIu;_t98Dd8nVxKA=QC}Kh8ZOeZ$(AW$mi7?l%%PDp;x^so zFE6dkZd$C-WYTYZ#3{XW6Hj^1U2oa0!p*N92s?}H)-zJPLt4%}@$@S_g*#$u1K$4D zuS8*g-bkgcS(g6}&>FVGogwQ0t0&*-R+PhQ9G$d0r-+VsG%2X^U4 z=Q9B(Th$aQ^pw#J*79-->etj&GK-kmeDgOU;prWvZ*)-E&+Jrp1TGIP{Md8?jkMl? zyTA4O)-i`!S!rOAG;yk7@)u(x6wmQ;s#7#~y*fkp`n2g5`5oDkG{r*U`?gZFzS%0Y zLC1gVpP)dPZS8la5>vyhgrZgUI;cR$QcC-_?A`<4}WYP?{Ekc zFMAjFR`|X4o25*d%7Qm$mrLjq0_3YC(>3%^0VP&JIcYwp_|mqI$Z@Hqi12$_xxuCv zxhU-F4&7{hN&TEs;`jz!{;d~*0g>Op>E2VbT%d@S};R*itfj%r8MFxIvm5J*yMsxfF;(-twt9lB#+jE?`Rnzql(-EuShJ6`3S&lu5!#(R4ND8qHE1z-B$H2xKKc-NdFOPO&3l z)AR-n|E=4D0&&H)!>e+kbL(a>H@q_%W%kIWXv>O9+Pw1EY#mTyJp815cCdh_1a{V|$FzIP7Rol-PJCT+mW-?|<-h*QC;`K8ct`XcH7TaMF ztZ|0~pXM^e>V1|8HLADotgRbJV}@%c!s6rO?g`TWU`QGk=b5l}-hYALSJvO+U}Ojj zWhWbJjBXM*33{r|K&arjosalpnvhIKnmNxmo87>o(UoR(MCuEp z97#049{;klB>KP?^?lA-PknPV9J~19i<4B`CENpKZ_3Y$cbq!f4OcK6*yDKeeqp-x zo>VT|P+y)@2JAha@Ge%Fa12YrLcpHDW12#@_QdYrwl(Y2v0XWlX+^mrQ<)oQSnrtHrill{?=DQgRt}3p%K$3#8D~Gc22PS zD|$V8SJoN3xI78#pbQREf=h{c>b2@JUEDYPq3vMh=)08djlPXpFFL%rIS5Sb>JPUY zY3CoQ-F1h?hsSQy{1u#NFxM2-eruS7zo(y8>)?zx7jE;3$IiNTdgy(ep6$)lhTc!w zQkrzHM$*ri_ik;gVIHvEe&tePYh(z&Hm`wt)?1+hj6%%joNM|yQ9Lbylb!tOmIwKr z7`~co4n6i_?b(5q=C~$9$W=R(*4f@dzmMj7ZFhr;(T-~}Oe`MtLrUM%rpG0QAJ$Z~ zEG!-MDz8Pdo6Nng;*Qa-T9QDFEy=!|`uvS5Ki7<70|EZ6Q-=b30&4}R9izQXk(ViW zF)f=DnKe&!Vcx-f^7{>+_n*hkENJp7f895~o zP4|}FweeP^^lY~%N?4SY-@-ZJ*(Ei$rO{-1g)`G1nmD&BUvW-$$7OoQ2L|4LbfhoW zd+`xJEUhhCkAd5R`pF~NZO>SG-lYoF8;NjTl1yMU;*2dHqzlc@wMsHP=ny$(Ec@>K z(V5Sbji02y{>d}gLHZTZ`5#2?&{(i@`PbX_IN$}h$o~unjqc~ku|L~)g!9hw)v$I$ zwm~`z?t$GXR~fCW1;O@(Z{l;>u__i;O*ZsQt~4*i7eu2Q&N81cC}sE(QH-Tu$Zq=B z71^npdyvQd+F`CLUH-jXi@NEPdeOeBqYw5vNfidad~`kic24|4SNFi)^y-7c-*5L~ zF6C;tUrf@VG|S|^r+?1iva>=&J?wG5r*5(-Nyua39M)XPmQG1kpn)6!>HQ%-0H(OtKH zzDUvg#q`D0?&zRnejCXCZ+%8Iz{juEH=JJQR$%sT+rv2W*)|2)WEkJ^UUqi9FKx-Z ztZ34fKzqEPy+>qHQ%*{Yv0A*ojE}y^TX4D~6`ry0T~6HPhnX3r)bb?J)(_(32OlfU{8>IIN={Cfs71n z9UuV&E)n2tfHgSd0Cv8~hXh<~?ZD9m@+FbHC65K51#5R#S4*(D3?A4L9o=oiC08Qb9Dv9m-#7x$&&ri67|fL`H#orSke>p31qY)Hu3^B! zV1D-U%k+_>ae$t+7VswltF~`t0tbc(`_pnjW)7H1a{E8c=}!tskkKTUuUJPA!CScl z1{2s&^0hY{gonTtY{jAiR47yo9GjmAkl$iGC~!D$h0!;Mw6jND^gKKuYLC?@=^@cLShtwCao9O+L)Y@yEi}*0>(5Q zZ6E+9`9P(?FO=oVq0nC>1K`##w_gdIN+dbDyD39wK=&w1p#dZSI4mfX1R94Th#@6} zkw{??7=m06z%2IwY^8^bJITe-3hsju2Y;e)2NKD{OA3Ji?EZWt?oPA^F9FP`jkh&8 z8@cu^4;wqUm8G>a82Fzph^&#kAJNth1|~NQ41n!^{s5K^2ejP|{!bYMG=hF`x1VJw ztRxPo(7H0{&pnxYz6)P zkuwqlet(onK(p6x^*}TWOup;N(1Z=ja2v=O4b5%8wTHw>LUYgWWq2r_zP=0%fQIYK z@c8w#05Ce>YJEKd04uI5lR!)UAp-#4`$GnjNQpnoHsC`N18}12`jC_WxX|@w1ZXaY ze!pZ-K>n#$1Ps6%uCIrOKzHljm4xQ}b!7ywMfiIe`1A>Xmf]" + ] + }, + "execution_count": 128, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "plt.plot(T,visc)\n", + "plt.plot(T,visc_lin)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Plot data" + ] + }, + { + "cell_type": "code", + "execution_count": 124, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[ 0 5 10 15 20 25 30 35 40]\n", + "[0.0006 0.0008 0.001 0.0012 0.0014 0.0016 0.0018 0.002 ]\n" + ] + } + ], + "source": [ + "x_increment = 5\n", + "x_ticks = np.arange(T0, T1 + x_increment, x_increment)\n", + "y_increment = 2e-4\n", + "y_ticks = np.arange(math.floor(visc.min() * 10000) / 10000, math.ceil(visc.max() * 10000) / 10000 + y_increment, y_increment)\n", + "print(x_ticks)\n", + "print(y_ticks)" + ] + }, + { + "cell_type": "code", + "execution_count": 154, + "metadata": {}, + "outputs": [ + { + "ename": "AttributeError", + "evalue": "'NoneType' object has no attribute 'add_text'", + "output_type": "error", + "traceback": [ + "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[1;31mAttributeError\u001b[0m Traceback (most recent call last)", + "\u001b[1;32m\u001b[0m in \u001b[0;36m\u001b[1;34m\u001b[0m\n\u001b[0;32m 27\u001b[0m \u001b[0mtext\u001b[0m \u001b[1;33m=\u001b[0m \u001b[1;34mr'$\\left ( T_0, \\mu_0 \\right )$'\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 28\u001b[0m \u001b[1;31m#ax.text(21, 0.00102, text, style='italic') #fontsize=6)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 29\u001b[1;33m \u001b[0mspnspecs\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0madd_text\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0max\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mtext\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mtext\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mx\u001b[0m\u001b[1;33m=\u001b[0m\u001b[1;36m0.5\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0my\u001b[0m\u001b[1;33m=\u001b[0m\u001b[1;36m0.5\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mtransform\u001b[0m\u001b[1;33m=\u001b[0m\u001b[1;32mFalse\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mbold\u001b[0m\u001b[1;33m=\u001b[0m\u001b[1;32mFalse\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mha\u001b[0m\u001b[1;33m=\u001b[0m\u001b[1;34m'left'\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mva\u001b[0m\u001b[1;33m=\u001b[0m\u001b[1;34m'top'\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 30\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 31\u001b[0m \u001b[1;32mif\u001b[0m \u001b[0mspnspecs\u001b[0m \u001b[1;32mis\u001b[0m \u001b[1;32mnot\u001b[0m \u001b[1;32mNone\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", + "\u001b[1;31mAttributeError\u001b[0m: 'NoneType' object has no attribute 'add_text'" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "fig, axes = plt.subplots(nrows=1, ncols=1, tight_layout=True, \n", + " figsize=(width/1.5, width/2))\n", + "\n", + "if not isinstance(axes, list):\n", + " axes = [axes]\n", + "\n", + "for idx, ax in enumerate(axes):\n", + " ax.set_xlim(T0, T1)\n", + " ax.set_ylim(y_ticks[0], y_ticks[-2])\n", + " ax.set_xticks(x_ticks)\n", + " ax.set_xticklabels(x_ticks)\n", + " #ax.set_yticks(y_ticks)\n", + " #ax.set_yticklabels(y_ticks, rotation=90, va='center')\n", + " if spnspecs is not None:\n", + " spnspecs.remove_edge_ticks(ax)\n", + " spnspecs.heading(ax, letter=letters[idx])\n", + " \n", + "\n", + "ax = axes[0]\n", + "#ax.set_aspect('square', adjustable='box')\n", + "#ax.axhline(0, lw=0.5, ls='-.', color='black')\n", + "#ax.axvline(0, lw=0.5, ls='-.', color='black')\n", + "\n", + "ax.plot(T, visc, lw=1.25, color='#00BFFF', label=r'$\\mu')\n", + "ax.plot(T, visc_lin, lw=1., color='black', ls='-', label=r'$\\frac {\\partial \\mu} {\\partial T}')\n", + "ax.plot([Tviscref], [mu_0], marker=\"o\", markersize=4, markeredgecolor=\"red\", markerfacecolor=\"blue\")\n", + "text = r'$\\left ( T_0, \\mu_0 \\right )$'\n", + "#ax.text(21, 0.00102, text, style='italic') #fontsize=6)\n", + "spnspecs.add_text(ax, text=text, x=0.5, y=0.5, transform=False, bold=False, ha='left', va='top')\n", + "\n", + "if spnspecs is not None:\n", + " handles, labels = ax.get_legend_handles_labels()\n", + " spnspecs.graph_legend(ax, handles=handles[::-1], labels=labels[::-1],\n", + " loc='upper right', bbox_to_anchor=(1.00,0.95),\n", + " handlelength=1.5)\n", + "ax.set_xlabel(r'Temperature, in $^\\circ$C')\n", + "ax.set_ylabel(r'$\\mu$, in $\\frac{kg}{m \\cdot s}$')\n", + "\n", + "fpth = os.path.join(figpth, 'VSCnonlinear.pdf')\n", + "fig.savefig(fpth, dpi=dpi)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.1" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/doc/SuppTechInfo/viscosity.tex b/doc/SuppTechInfo/viscosity.tex new file mode 100644 index 00000000000..49fdebe18c8 --- /dev/null +++ b/doc/SuppTechInfo/viscosity.tex @@ -0,0 +1,74 @@ + +The dynamic viscosity of a fluid is a function of fluid composition and temperature and typically has a weak dependence on pressure. In some applications, particularly those that involve large variations in temperature, viscosity can vary significantly in space and over time. Variations in viscosity, in turn, can affect groundwater flow by changing the hydraulic conductivity, which is inversely proportional to viscosity. In the original release of \mf, variations in viscosity and their effects on hydraulic conductivity were not taken into account. The \mf Viscosity (VSC) Package allows viscosity to vary as a function of solute concentrations and fluid temperature and accounts for the effect of variable viscosity on hydraulic conductivity in the Node-Property Flow (NPF) Package and in stress packages that involve head-dependent groundwater flow. Temperature-dependent viscosity has been implemented in other groundwater modeling codes, including HST3D \citep{kipp1987}, VS2DH \citep{healy1996}, SUTRA-MS \citep{hughes2004}, and SEAWAT Version 4 \citep{langevin2008seawat}. Like these other codes, the VSC Package does not account for the dependence of viscosity on pressure, which is negligible in most applications. + +For cases in which the rate of groundwater flow is proportional to the head gradient in the direction of flow, as in an isotropic porous medium, the flow between two adjacent locations can be approximated using a one-dimensional form of Darcy's Law \citep[equation 4-15]{modflow6gwf}: + +\begin{equation} +\label{eqn:modflow6gwf-4-15} +Q = C \left( h_{1} - h_{2} \right), +\end{equation} + +\noindent where $h_{1}$ and $h_{2}$ are the hydraulic heads at locations 1 and 2, respectively, $Q$ is the flow from location 1 to location 2, and $C$ is a hydraulic conductance defined by~\citep[equation 4-14]{modflow6gwf}: + +\begin{equation} +\label{eqn:modflowgwf-4-14} +C = \frac{K A} {L_{1,2}}, +\end{equation} + +\noindent where $K$ is the hydraulic conductivity of the porous medium that connects the two locations, $A$ is the cross-sectional area perpendicular to flow, and $L_{1,2}$ is the distance between two locations. A form of equation~\ref{eqn:modflow6gwf-4-15} is used in the \mf ``conductance-based'' formulation for flow between two adjacent model cells \citep{modflow6gwf}. In that case, the conductance is based on an effective hydraulic conductivity between the two cells, the area of the cell-cell interface, and the distance between the cell centers. A form of equation~\ref{eqn:modflow6gwf-4-15} is also used in \mf stress packages in which the flow into or out of the model at a model boundary is head-dependent. For example, in the General-Head Boundary (GHB) Package, flow between an external source and a groundwater model cell is proportional to the difference between the head assigned to the external source and the simulated head in the cell, and the conductance is representative of the material between the external source and the cell center. + +Because hydraulic conductivity is inversely proportional to viscosity, the conductivity $K$ for a simulated fluid composition and temperature is related to the conductivity $K_{0}$ for some reference composition and temperature by + +\begin{equation} +\label{eqn:conductivity-ratio} +K = \frac{\mu_{0}}{\mu} K_{0}. +\end{equation} + +\noindent It follows from equation~\ref{eqn:modflowgwf-4-14} that the conductance $C$ for a simulated fluid composition and temperature is related to the conductance $C_{0}$ for the reference composition and temperature by + +\begin{equation} +\label{eqn:conductance-ratio} +C = \frac{\mu_{0}}{\mu} C_{0}, +\end{equation} + +\noindent where $\mu$ and $\mu_{0}$ are the viscosities at the simulated and reference conditions, respectively. The VSC Package uses equations~\ref{eqn:conductivity-ratio} and~\ref{eqn:conductance-ratio} to update hydraulic conductivities and conductances to account for changes in solute concentrations and temperature during a flow and transport simulation based on the Groundwater Flow (GWF) and Groundwater Transport (GWT) Models. The GWT Model is designed to simulate solute transport but can be used to simulate heat transport in some applications by appropriately scaling the input parameters to render the solute-transport equation mathematically equivalent to the heat-transport equation \citep{zheng2010supplemental}. In such cases, the ``solute concentration'' calculated by a GWT-based heat-transport model can by identified in the VSC Package input file as representing temperature. Although the VSC Package allows viscosity to vary with solute concentration, variations in viscosity are typically most significant in heat-transport applications. + +\subsection{Dependence of Viscosity on Concentration and Temperature} \label{sec:fluidvsc} + +The VSC Package calculates viscosity as a function of concentration and temperature using the following equation \citep[equation 17]{langevin2008seawat}: + +\begin{equation} +\label{eqn:viscfunc} +\mu = \mu_T \left ( T \right ) + \sum_{k=1}^{NS} \frac{\partial \mu} {\partial C^k} \left ( C^k - C_{0}^k \right ) +\end{equation} + +\noindent where $NS$ is the number of chemical species (solutes) whose concentrations affect viscosity, $C^k$ and $C_{0}^k$ are the simulated and user-specified reference concentrations of species $k$, respectively, and $\partial \mu / \partial C^k$ is the user-specified rate at which viscosity changes linearly with the concentration of species $k$. (Symbols $C^k$ and $C_{0}^k$ are not to be confused with the symbols for conducance, $C$ and $C_0$, introduced earlier.) When all concentrations are equal to their reference values, the viscosity is equal to $\mu_T \left ( T \right )$, which embodies the dependence of viscosity on temperature according to + +\begin{align} + \label{eqn:viscT} + \mu_T \left ( T \right ) = \begin{dcases} + \mu_{0} + \frac{\partial \mu} {\partial T} \left ( T - T_{0}\right ) & linear \\ + \mu_{0} A_2^{\frac{A_3 \left ( T_0 - T \right )} {\left ( T + A_4 \right ) \left ( T_0 + A_4 \right ) }} & nonlinear + \end{dcases} . +\end{align} + +\noindent where $T$ and $T_{0}$ are the simulated and user-specified reference temperature, respectively, and $\mu_{0}$ is the user-specified reference viscosity. The reference viscosity is commonly set to 0.001 kg~m$^{-1}$~s$^{-1}$, which is representative of freshwater at a reference temperature of 20$^{\circ}$C \citep[Table 11.1]{maidment1993}. When $T$ equals $T_{0}$, or if temperature is not designated by the user as affecting viscosity, $\mu_T \left ( T \right )$ equals $\mu_{0}$. Equation~\ref{eqn:viscT} includes both linear and nonlinear variation of viscosity with temperature. Linear variation is the default, in which case $\partial \mu / \partial T$ is the user-specified rate at which viscosity changes linearly with temperature. Nonlinear variation is a user-selectable option, in which case the variation of viscosity from the reference value is determined by user-specified parameters $A_2$, $A_3$, and $A_4$. The nonlinear option in equation~\ref{eqn:viscT} is mathematically equivalent to one of the options available in SEAWAT Version 4 \citep[equation 18]{langevin2002seawat} but is written in a form that explicitly sets $\mu_T \left ( T \right )$ equal to $\mu_{0}$ when $T$ equals $T_{0}$. Setting $\mu_0$, $A_2$, $A_3$, and $A_4$ to 0.001002 kg~m$^{-1}$~s$^{-1}$, 10.0, 248.37, and 133.15, respectively, and rounding the lead coefficient to one decimal place gives the temperature dependence of viscosity used in SUTRA \citep{voss1984sutra} and SUTRA-MS \citep{hughes2004}. VS2DH \citep{healy1996} also calculate the temperature dependence of viscosity using functions that are, respectively, mathematically equivalent to and a special case of equation~\ref{eqn:viscT}. + +Over the temperature range $0-40^{\circ}$C, the viscosity of freshwater varies between approximately 0.0007 and 0.00175 kg~m$^{-1}$~s$^{-1}$, as indicated by the nonlinear (blue) curve in figure~\ref{fig:viscosityrelation}. The black line in figure~\ref{fig:viscosityrelation} depicts a linear approximation that coincides with the nonlinear curve at the reference temperature and viscosity, $\left ( T_0, \mu_0 \right ) = \left(\right.$20$^{\circ}$C, 0.001 kg~m$^{-1}$~s$^{-1}\left.\right)$. + +\begin{figure} + \begin{center} + \includegraphics{./Figures/VSCnonlinear.pdf} + \caption[Graph showing the nonlinear response in the viscosity as temperature changes]{Nonlinear dependence of viscosity on temperature (blue curve) that is representative of freshwater over the temperature range $0-40^{\circ}$C, and a linear approximation (black curve) that has the same value $\left ( \mu_0 \right )$ and slope at the reference temperature $\left ( T_0 \right )$. The blue curve is generated using 0.001002 kg~m$^{-1}$~s$^{-1}$,10.0, 248.37, and 133.15 for $\mu_0$, $A_2$, $A_3$, and $A_4$, respectively} + \label{fig:viscosityrelation} + \end{center} +\end{figure} + +\subsection{Accounting for Variable Viscosity in Flows Between Cells} \label{sec:gwfvsc} + +The VSC Package uses concentrations and temperatures calculated for each cell by a GWT model on the previous time step or outer solution iteration to calculate viscosities for the current time step. These viscosities are used to adjust cell hydraulic conductivity values in the NPF Package using equation~\ref{eqn:conductivity-ratio}. The reference values of conductivity are the values specified by the user in the NPF Package and, optionally, the Time-Varying Conductivity (TVK) Package (Chapter 6 of this document). The conductivity adjustment is performed after the user-specified conductivities are read in but before conductivity values are passed to program units that use cell conductivities to formulate expressions for flow between cells, which include the ``conductance-based'' formulation for flow and the XT3D capability \citep{modflow6xt3d}. If the VSC Package is not active, cell conductivities are not adjusted for variable viscosity, and the user-specified values are used. + +\subsection{Accounting for Variable Viscosity in Boundary Flows} \label{sec:gwfvsc} + +The VSC Package uses concentrations and temperatures calculated for each cell by a GWT model on the previous time step or outer solution iteration in calculating viscosities for the current time step. These viscosities are used to adjust hydraulic conductances, using equation~\ref{eqn:conductance-ratio}, in stress packages that involve head-dependent boundary flows, which include the River (RIV), General-Head Boundary (GHB), Drain (DRN), Streamflow Routing (SFR), Lake (LAK), and Multi-Aquifer Well (MAW) Packages \citep{modflow6gwf}. For a boundary flow out of the model, the viscosity is based on the simulated concentration or temperature in the cell from which the boundary flow originates. For a boundary flow into the model, the viscosity is based on the concentration or temperature of the water entering the cell from the boundary. The reference values of conductance are the values normally set or calculated by the stress package based on user input. The conductance adjustment is performed after the stress package completes its normal conductance calculation but before the conductance value is used to formulate the expression for the boundary flow. If the VSC Package is not active, stress-package conductances are not adjusted for variable viscosity. + From a0cbfc65a131d369cc7a39cbda314d292c51ec50 Mon Sep 17 00:00:00 2001 From: Eric Morway Date: Fri, 28 Oct 2022 08:38:23 -0700 Subject: [PATCH 11/31] forgot to update makefile in ./make --- make/makefile | 2 ++ src/meson.build | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/make/makefile b/make/makefile index 83a6bc5c5fb..50ebe4e3b61 100644 --- a/make/makefile +++ b/make/makefile @@ -166,6 +166,7 @@ $(OBJDIR)/Mover.o \ $(OBJDIR)/GwfMvrPeriodData.o \ $(OBJDIR)/ims8misc.o \ $(OBJDIR)/GwfBuyInputData.o \ +$(OBJDIR)/GwfVscInputData.o \ $(OBJDIR)/InterfaceMap.o \ $(OBJDIR)/gwf3disu8.o \ $(OBJDIR)/GridSorting.o \ @@ -204,6 +205,7 @@ $(OBJDIR)/GhostNode.o \ $(OBJDIR)/gwf3ghb8.o \ $(OBJDIR)/gwf3evt8.o \ $(OBJDIR)/gwf3drn8.o \ +$(OBJDIR)/gwf3vsc8.o \ $(OBJDIR)/gwf3chd8.o \ $(OBJDIR)/ims8reordering.o \ $(OBJDIR)/GridConnection.o \ diff --git a/src/meson.build b/src/meson.build index a922990a2c8..88f6095d6d5 100644 --- a/src/meson.build +++ b/src/meson.build @@ -34,7 +34,6 @@ modflow_sources = files( 'Model' / 'GroundWaterFlow' / 'gwf3.f90', 'Model' / 'GroundWaterFlow' / 'gwf3api8.f90', 'Model' / 'GroundWaterFlow' / 'gwf3buy8.f90', - 'Model' / 'GroundWaterFlow' / 'gwf3vsc8.f90', 'Model' / 'GroundWaterFlow' / 'gwf3chd8.f90', 'Model' / 'GroundWaterFlow' / 'gwf3csub8.f90', 'Model' / 'GroundWaterFlow' / 'gwf3dis8.f90', @@ -63,6 +62,7 @@ modflow_sources = files( 'Model' / 'GroundWaterFlow' / 'gwf3tvk8.f90', 'Model' / 'GroundWaterFlow' / 'gwf3tvs8.f90', 'Model' / 'GroundWaterFlow' / 'gwf3uzf8.f90', + 'Model' / 'GroundWaterFlow' / 'gwf3vsc8.f90', 'Model' / 'GroundWaterFlow' / 'gwf3wel8.f90', 'Model' / 'GroundWaterTransport' / 'gwt1.f90', 'Model' / 'GroundWaterTransport' / 'gwt1adv1.f90', From 3315d85af440ce50121ba0f6821e685b9e79529b Mon Sep 17 00:00:00 2001 From: Eric Morway Date: Fri, 28 Oct 2022 14:31:42 -0700 Subject: [PATCH 12/31] fix 2 deallocations --- src/Model/Connection/GwfInterfaceModel.f90 | 1 + src/Model/GroundWaterFlow/gwf3maw8.f90 | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Model/Connection/GwfInterfaceModel.f90 b/src/Model/Connection/GwfInterfaceModel.f90 index 8735a32c9e8..b8f71e6cdb3 100644 --- a/src/Model/Connection/GwfInterfaceModel.f90 +++ b/src/Model/Connection/GwfInterfaceModel.f90 @@ -147,6 +147,7 @@ subroutine gwfifm_da(this) call mem_deallocate(this%inobs) call mem_deallocate(this%innpf) call mem_deallocate(this%inbuy) + call mem_deallocate(this%invsc) call mem_deallocate(this%insto) call mem_deallocate(this%incsub) call mem_deallocate(this%inmvr) diff --git a/src/Model/GroundWaterFlow/gwf3maw8.f90 b/src/Model/GroundWaterFlow/gwf3maw8.f90 index f586bf12319..45d54c678bd 100644 --- a/src/Model/GroundWaterFlow/gwf3maw8.f90 +++ b/src/Model/GroundWaterFlow/gwf3maw8.f90 @@ -3043,7 +3043,6 @@ subroutine maw_da(this) call mem_deallocate(this%kappa) call mem_deallocate(this%cbcauxitems) call mem_deallocate(this%idense) - call mem_deallocate(this%viscratios) ! ! -- pointers to gwf variables nullify (this%gwfiss) From a73c8787958373dbbf54152c1f66b3705096d107 Mon Sep 17 00:00:00 2001 From: Eric Morway Date: Sat, 29 Oct 2022 08:37:50 -0700 Subject: [PATCH 13/31] Update makefile with the one from #1067 --- make/makefile | 50 +++++++++++++++++++++++++++++--------------------- 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/make/makefile b/make/makefile index 50ebe4e3b61..e6ed82d2e9d 100644 --- a/make/makefile +++ b/make/makefile @@ -6,27 +6,28 @@ include ./makedefaults # Define the source file directories SOURCEDIR1=../src SOURCEDIR2=../src/Exchange -SOURCEDIR3=../src/Model -SOURCEDIR4=../src/Model/Connection -SOURCEDIR5=../src/Model/Geometry -SOURCEDIR6=../src/Model/GroundWaterFlow -SOURCEDIR7=../src/Model/GroundWaterTransport -SOURCEDIR8=../src/Model/ModelUtilities -SOURCEDIR9=../src/Solution -SOURCEDIR10=../src/Solution/LinearMethods -SOURCEDIR11=../src/Timing -SOURCEDIR12=../src/Utilities -SOURCEDIR13=../src/Utilities/Idm -SOURCEDIR14=../src/Utilities/Libraries -SOURCEDIR15=../src/Utilities/Libraries/blas +SOURCEDIR3=../src/Solution +SOURCEDIR4=../src/Solution/LinearMethods +SOURCEDIR5=../src/Timing +SOURCEDIR6=../src/Utilities +SOURCEDIR7=../src/Utilities/Idm +SOURCEDIR8=../src/Utilities/TimeSeries +SOURCEDIR9=../src/Utilities/Memory +SOURCEDIR10=../src/Utilities/OutputControl +SOURCEDIR11=../src/Utilities/ArrayRead +SOURCEDIR12=../src/Utilities/Libraries +SOURCEDIR13=../src/Utilities/Libraries/rcm +SOURCEDIR14=../src/Utilities/Libraries/blas +SOURCEDIR15=../src/Utilities/Libraries/sparskit2 SOURCEDIR16=../src/Utilities/Libraries/daglib -SOURCEDIR17=../src/Utilities/Libraries/rcm -SOURCEDIR18=../src/Utilities/Libraries/sparsekit -SOURCEDIR19=../src/Utilities/Libraries/sparskit2 -SOURCEDIR20=../src/Utilities/Memory -SOURCEDIR21=../src/Utilities/Observation -SOURCEDIR22=../src/Utilities/OutputControl -SOURCEDIR23=../src/Utilities/TimeSeries +SOURCEDIR17=../src/Utilities/Libraries/sparsekit +SOURCEDIR18=../src/Utilities/Observation +SOURCEDIR19=../src/Model +SOURCEDIR20=../src/Model/Connection +SOURCEDIR21=../src/Model/GroundWaterTransport +SOURCEDIR22=../src/Model/ModelUtilities +SOURCEDIR23=../src/Model/GroundWaterFlow +SOURCEDIR24=../src/Model/Geometry VPATH = \ ${SOURCEDIR1} \ @@ -51,7 +52,8 @@ ${SOURCEDIR19} \ ${SOURCEDIR20} \ ${SOURCEDIR21} \ ${SOURCEDIR22} \ -${SOURCEDIR23} +${SOURCEDIR23} \ +${SOURCEDIR24} .SUFFIXES: .f90 .F90 .o @@ -98,6 +100,7 @@ $(OBJDIR)/TimeArraySeries.o \ $(OBJDIR)/ObsOutputList.o \ $(OBJDIR)/Observe.o \ $(OBJDIR)/InputDefinition.o \ +$(OBJDIR)/ArrayReaderBase.o \ $(OBJDIR)/TimeArraySeriesLink.o \ $(OBJDIR)/ObsUtility.o \ $(OBJDIR)/ObsContainer.o \ @@ -107,6 +110,7 @@ $(OBJDIR)/gwf3npf8idm.o \ $(OBJDIR)/gwf3disv8idm.o \ $(OBJDIR)/gwf3disu8idm.o \ $(OBJDIR)/gwf3dis8idm.o \ +$(OBJDIR)/Integer2dReader.o \ $(OBJDIR)/TimeArraySeriesManager.o \ $(OBJDIR)/PackageMover.o \ $(OBJDIR)/Obs3.o \ @@ -116,11 +120,15 @@ $(OBJDIR)/BudgetFileReader.o \ $(OBJDIR)/StructVector.o \ $(OBJDIR)/IdmLogger.o \ $(OBJDIR)/InputDefinitionSelector.o \ +$(OBJDIR)/Integer1dReader.o \ +$(OBJDIR)/Double2dReader.o \ +$(OBJDIR)/Double1dReader.o \ $(OBJDIR)/BoundaryPackage.o \ $(OBJDIR)/BaseModel.o \ $(OBJDIR)/BudgetTerm.o \ $(OBJDIR)/StructArray.o \ $(OBJDIR)/ModflowInput.o \ +$(OBJDIR)/LayeredArrayReader.o \ $(OBJDIR)/NumericalModel.o \ $(OBJDIR)/mf6lists.o \ $(OBJDIR)/PackageBudget.o \ From 3a3287cbae39c4d1eaeef0bfc663394b053662a5 Mon Sep 17 00:00:00 2001 From: Eric Morway Date: Sat, 29 Oct 2022 08:45:30 -0700 Subject: [PATCH 14/31] makefile --- make/makefile | 86 +++++++++++++++++++++++---------------------------- 1 file changed, 39 insertions(+), 47 deletions(-) diff --git a/make/makefile b/make/makefile index e6ed82d2e9d..2d75781893a 100644 --- a/make/makefile +++ b/make/makefile @@ -4,30 +4,29 @@ include ./makedefaults # Define the source file directories -SOURCEDIR1=../src -SOURCEDIR2=../src/Exchange -SOURCEDIR3=../src/Solution -SOURCEDIR4=../src/Solution/LinearMethods -SOURCEDIR5=../src/Timing -SOURCEDIR6=../src/Utilities -SOURCEDIR7=../src/Utilities/Idm -SOURCEDIR8=../src/Utilities/TimeSeries -SOURCEDIR9=../src/Utilities/Memory -SOURCEDIR10=../src/Utilities/OutputControl -SOURCEDIR11=../src/Utilities/ArrayRead -SOURCEDIR12=../src/Utilities/Libraries -SOURCEDIR13=../src/Utilities/Libraries/rcm -SOURCEDIR14=../src/Utilities/Libraries/blas -SOURCEDIR15=../src/Utilities/Libraries/sparskit2 -SOURCEDIR16=../src/Utilities/Libraries/daglib -SOURCEDIR17=../src/Utilities/Libraries/sparsekit -SOURCEDIR18=../src/Utilities/Observation -SOURCEDIR19=../src/Model -SOURCEDIR20=../src/Model/Connection -SOURCEDIR21=../src/Model/GroundWaterTransport -SOURCEDIR22=../src/Model/ModelUtilities -SOURCEDIR23=../src/Model/GroundWaterFlow -SOURCEDIR24=../src/Model/Geometry +SOURCEDIR1=..\src +SOURCEDIR2=..\src\Exchange +SOURCEDIR3=..\src\Model +SOURCEDIR4=..\src\Model\Connection +SOURCEDIR5=..\src\Model\Geometry +SOURCEDIR6=..\src\Model\GroundWaterFlow +SOURCEDIR7=..\src\Model\GroundWaterTransport +SOURCEDIR8=..\src\Model\ModelUtilities +SOURCEDIR9=..\src\Solution +SOURCEDIR10=..\src\Solution\LinearMethods +SOURCEDIR11=..\src\Timing +SOURCEDIR12=..\src\Utilities +SOURCEDIR13=..\src\Utilities\Idm +SOURCEDIR14=..\src\Utilities\Libraries +SOURCEDIR15=..\src\Utilities\Libraries\blas +SOURCEDIR16=..\src\Utilities\Libraries\daglib +SOURCEDIR17=..\src\Utilities\Libraries\rcm +SOURCEDIR18=..\src\Utilities\Libraries\sparsekit +SOURCEDIR19=..\src\Utilities\Libraries\sparskit2 +SOURCEDIR20=..\src\Utilities\Memory +SOURCEDIR21=..\src\Utilities\Observation +SOURCEDIR22=..\src\Utilities\OutputControl +SOURCEDIR23=..\src\Utilities\TimeSeries VPATH = \ ${SOURCEDIR1} \ @@ -52,8 +51,7 @@ ${SOURCEDIR19} \ ${SOURCEDIR20} \ ${SOURCEDIR21} \ ${SOURCEDIR22} \ -${SOURCEDIR23} \ -${SOURCEDIR24} +${SOURCEDIR23} .SUFFIXES: .f90 .F90 .o @@ -100,7 +98,6 @@ $(OBJDIR)/TimeArraySeries.o \ $(OBJDIR)/ObsOutputList.o \ $(OBJDIR)/Observe.o \ $(OBJDIR)/InputDefinition.o \ -$(OBJDIR)/ArrayReaderBase.o \ $(OBJDIR)/TimeArraySeriesLink.o \ $(OBJDIR)/ObsUtility.o \ $(OBJDIR)/ObsContainer.o \ @@ -110,48 +107,51 @@ $(OBJDIR)/gwf3npf8idm.o \ $(OBJDIR)/gwf3disv8idm.o \ $(OBJDIR)/gwf3disu8idm.o \ $(OBJDIR)/gwf3dis8idm.o \ -$(OBJDIR)/Integer2dReader.o \ +$(OBJDIR)/BudgetFileReader.o \ $(OBJDIR)/TimeArraySeriesManager.o \ $(OBJDIR)/PackageMover.o \ $(OBJDIR)/Obs3.o \ $(OBJDIR)/NumericalPackage.o \ $(OBJDIR)/Budget.o \ -$(OBJDIR)/BudgetFileReader.o \ $(OBJDIR)/StructVector.o \ $(OBJDIR)/IdmLogger.o \ $(OBJDIR)/InputDefinitionSelector.o \ -$(OBJDIR)/Integer1dReader.o \ -$(OBJDIR)/Double2dReader.o \ -$(OBJDIR)/Double1dReader.o \ +$(OBJDIR)/sort.o \ +$(OBJDIR)/SfrCrossSectionUtils.o \ +$(OBJDIR)/BudgetTerm.o \ $(OBJDIR)/BoundaryPackage.o \ $(OBJDIR)/BaseModel.o \ -$(OBJDIR)/BudgetTerm.o \ $(OBJDIR)/StructArray.o \ $(OBJDIR)/ModflowInput.o \ -$(OBJDIR)/LayeredArrayReader.o \ +$(OBJDIR)/SfrCrossSectionManager.o \ +$(OBJDIR)/dag_module.o \ +$(OBJDIR)/BudgetObject.o \ $(OBJDIR)/NumericalModel.o \ $(OBJDIR)/mf6lists.o \ $(OBJDIR)/PackageBudget.o \ $(OBJDIR)/HeadFileReader.o \ -$(OBJDIR)/BudgetObject.o \ -$(OBJDIR)/sort.o \ -$(OBJDIR)/SfrCrossSectionUtils.o \ $(OBJDIR)/PrintSaveManager.o \ $(OBJDIR)/Xt3dAlgorithm.o \ $(OBJDIR)/gwf3tvbase8.o \ $(OBJDIR)/LoadMf6FileType.o \ +$(OBJDIR)/gwf3sfr8.o \ +$(OBJDIR)/gwf3riv8.o \ +$(OBJDIR)/gwf3maw8.o \ +$(OBJDIR)/gwf3lak8.o \ +$(OBJDIR)/GwfVscInputData.o \ +$(OBJDIR)/gwf3ghb8.o \ +$(OBJDIR)/gwf3drn8.o \ $(OBJDIR)/DistributedModel.o \ $(OBJDIR)/BaseExchange.o \ $(OBJDIR)/UzfCellGroup.o \ $(OBJDIR)/gwt1fmi1.o \ -$(OBJDIR)/SfrCrossSectionManager.o \ -$(OBJDIR)/dag_module.o \ $(OBJDIR)/OutputControlData.o \ $(OBJDIR)/gwf3ic8.o \ $(OBJDIR)/Xt3dInterface.o \ $(OBJDIR)/gwf3tvk8.o \ $(OBJDIR)/MemoryManagerExt.o \ $(OBJDIR)/IdmMf6FileLoader.o \ +$(OBJDIR)/gwf3vsc8.o \ $(OBJDIR)/GwfNpfOptions.o \ $(OBJDIR)/CellWithNbrs.o \ $(OBJDIR)/NumericalExchange.o \ @@ -159,11 +159,8 @@ $(OBJDIR)/Iunit.o \ $(OBJDIR)/gwf3uzf8.o \ $(OBJDIR)/gwt1apt1.o \ $(OBJDIR)/GwtSpc.o \ -$(OBJDIR)/gwf3sfr8.o \ $(OBJDIR)/OutputControl.o \ $(OBJDIR)/gwt1ic1.o \ -$(OBJDIR)/gwf3maw8.o \ -$(OBJDIR)/gwf3lak8.o \ $(OBJDIR)/gwt1mst1.o \ $(OBJDIR)/GwtDspOptions.o \ $(OBJDIR)/gwf3npf8.o \ @@ -174,7 +171,6 @@ $(OBJDIR)/Mover.o \ $(OBJDIR)/GwfMvrPeriodData.o \ $(OBJDIR)/ims8misc.o \ $(OBJDIR)/GwfBuyInputData.o \ -$(OBJDIR)/GwfVscInputData.o \ $(OBJDIR)/InterfaceMap.o \ $(OBJDIR)/gwf3disu8.o \ $(OBJDIR)/GridSorting.o \ @@ -200,7 +196,6 @@ $(OBJDIR)/gwf3disv8.o \ $(OBJDIR)/gwf3dis8.o \ $(OBJDIR)/gwf3api8.o \ $(OBJDIR)/gwf3wel8.o \ -$(OBJDIR)/gwf3riv8.o \ $(OBJDIR)/gwf3rch8.o \ $(OBJDIR)/gwf3sto8.o \ $(OBJDIR)/gwf3oc8.o \ @@ -210,10 +205,7 @@ $(OBJDIR)/gwf3hfb8.o \ $(OBJDIR)/gwf3csub8.o \ $(OBJDIR)/gwf3buy8.o \ $(OBJDIR)/GhostNode.o \ -$(OBJDIR)/gwf3ghb8.o \ $(OBJDIR)/gwf3evt8.o \ -$(OBJDIR)/gwf3drn8.o \ -$(OBJDIR)/gwf3vsc8.o \ $(OBJDIR)/gwf3chd8.o \ $(OBJDIR)/ims8reordering.o \ $(OBJDIR)/GridConnection.o \ From 35d798d085806ba1d307f2411cccdab6f542af2d Mon Sep 17 00:00:00 2001 From: Eric Morway Date: Sat, 29 Oct 2022 08:47:59 -0700 Subject: [PATCH 15/31] Revert "makefile" This reverts commit 3a3287cbae39c4d1eaeef0bfc663394b053662a5. --- make/makefile | 86 ++++++++++++++++++++++++++++----------------------- 1 file changed, 47 insertions(+), 39 deletions(-) diff --git a/make/makefile b/make/makefile index 2d75781893a..e6ed82d2e9d 100644 --- a/make/makefile +++ b/make/makefile @@ -4,29 +4,30 @@ include ./makedefaults # Define the source file directories -SOURCEDIR1=..\src -SOURCEDIR2=..\src\Exchange -SOURCEDIR3=..\src\Model -SOURCEDIR4=..\src\Model\Connection -SOURCEDIR5=..\src\Model\Geometry -SOURCEDIR6=..\src\Model\GroundWaterFlow -SOURCEDIR7=..\src\Model\GroundWaterTransport -SOURCEDIR8=..\src\Model\ModelUtilities -SOURCEDIR9=..\src\Solution -SOURCEDIR10=..\src\Solution\LinearMethods -SOURCEDIR11=..\src\Timing -SOURCEDIR12=..\src\Utilities -SOURCEDIR13=..\src\Utilities\Idm -SOURCEDIR14=..\src\Utilities\Libraries -SOURCEDIR15=..\src\Utilities\Libraries\blas -SOURCEDIR16=..\src\Utilities\Libraries\daglib -SOURCEDIR17=..\src\Utilities\Libraries\rcm -SOURCEDIR18=..\src\Utilities\Libraries\sparsekit -SOURCEDIR19=..\src\Utilities\Libraries\sparskit2 -SOURCEDIR20=..\src\Utilities\Memory -SOURCEDIR21=..\src\Utilities\Observation -SOURCEDIR22=..\src\Utilities\OutputControl -SOURCEDIR23=..\src\Utilities\TimeSeries +SOURCEDIR1=../src +SOURCEDIR2=../src/Exchange +SOURCEDIR3=../src/Solution +SOURCEDIR4=../src/Solution/LinearMethods +SOURCEDIR5=../src/Timing +SOURCEDIR6=../src/Utilities +SOURCEDIR7=../src/Utilities/Idm +SOURCEDIR8=../src/Utilities/TimeSeries +SOURCEDIR9=../src/Utilities/Memory +SOURCEDIR10=../src/Utilities/OutputControl +SOURCEDIR11=../src/Utilities/ArrayRead +SOURCEDIR12=../src/Utilities/Libraries +SOURCEDIR13=../src/Utilities/Libraries/rcm +SOURCEDIR14=../src/Utilities/Libraries/blas +SOURCEDIR15=../src/Utilities/Libraries/sparskit2 +SOURCEDIR16=../src/Utilities/Libraries/daglib +SOURCEDIR17=../src/Utilities/Libraries/sparsekit +SOURCEDIR18=../src/Utilities/Observation +SOURCEDIR19=../src/Model +SOURCEDIR20=../src/Model/Connection +SOURCEDIR21=../src/Model/GroundWaterTransport +SOURCEDIR22=../src/Model/ModelUtilities +SOURCEDIR23=../src/Model/GroundWaterFlow +SOURCEDIR24=../src/Model/Geometry VPATH = \ ${SOURCEDIR1} \ @@ -51,7 +52,8 @@ ${SOURCEDIR19} \ ${SOURCEDIR20} \ ${SOURCEDIR21} \ ${SOURCEDIR22} \ -${SOURCEDIR23} +${SOURCEDIR23} \ +${SOURCEDIR24} .SUFFIXES: .f90 .F90 .o @@ -98,6 +100,7 @@ $(OBJDIR)/TimeArraySeries.o \ $(OBJDIR)/ObsOutputList.o \ $(OBJDIR)/Observe.o \ $(OBJDIR)/InputDefinition.o \ +$(OBJDIR)/ArrayReaderBase.o \ $(OBJDIR)/TimeArraySeriesLink.o \ $(OBJDIR)/ObsUtility.o \ $(OBJDIR)/ObsContainer.o \ @@ -107,51 +110,48 @@ $(OBJDIR)/gwf3npf8idm.o \ $(OBJDIR)/gwf3disv8idm.o \ $(OBJDIR)/gwf3disu8idm.o \ $(OBJDIR)/gwf3dis8idm.o \ -$(OBJDIR)/BudgetFileReader.o \ +$(OBJDIR)/Integer2dReader.o \ $(OBJDIR)/TimeArraySeriesManager.o \ $(OBJDIR)/PackageMover.o \ $(OBJDIR)/Obs3.o \ $(OBJDIR)/NumericalPackage.o \ $(OBJDIR)/Budget.o \ +$(OBJDIR)/BudgetFileReader.o \ $(OBJDIR)/StructVector.o \ $(OBJDIR)/IdmLogger.o \ $(OBJDIR)/InputDefinitionSelector.o \ -$(OBJDIR)/sort.o \ -$(OBJDIR)/SfrCrossSectionUtils.o \ -$(OBJDIR)/BudgetTerm.o \ +$(OBJDIR)/Integer1dReader.o \ +$(OBJDIR)/Double2dReader.o \ +$(OBJDIR)/Double1dReader.o \ $(OBJDIR)/BoundaryPackage.o \ $(OBJDIR)/BaseModel.o \ +$(OBJDIR)/BudgetTerm.o \ $(OBJDIR)/StructArray.o \ $(OBJDIR)/ModflowInput.o \ -$(OBJDIR)/SfrCrossSectionManager.o \ -$(OBJDIR)/dag_module.o \ -$(OBJDIR)/BudgetObject.o \ +$(OBJDIR)/LayeredArrayReader.o \ $(OBJDIR)/NumericalModel.o \ $(OBJDIR)/mf6lists.o \ $(OBJDIR)/PackageBudget.o \ $(OBJDIR)/HeadFileReader.o \ +$(OBJDIR)/BudgetObject.o \ +$(OBJDIR)/sort.o \ +$(OBJDIR)/SfrCrossSectionUtils.o \ $(OBJDIR)/PrintSaveManager.o \ $(OBJDIR)/Xt3dAlgorithm.o \ $(OBJDIR)/gwf3tvbase8.o \ $(OBJDIR)/LoadMf6FileType.o \ -$(OBJDIR)/gwf3sfr8.o \ -$(OBJDIR)/gwf3riv8.o \ -$(OBJDIR)/gwf3maw8.o \ -$(OBJDIR)/gwf3lak8.o \ -$(OBJDIR)/GwfVscInputData.o \ -$(OBJDIR)/gwf3ghb8.o \ -$(OBJDIR)/gwf3drn8.o \ $(OBJDIR)/DistributedModel.o \ $(OBJDIR)/BaseExchange.o \ $(OBJDIR)/UzfCellGroup.o \ $(OBJDIR)/gwt1fmi1.o \ +$(OBJDIR)/SfrCrossSectionManager.o \ +$(OBJDIR)/dag_module.o \ $(OBJDIR)/OutputControlData.o \ $(OBJDIR)/gwf3ic8.o \ $(OBJDIR)/Xt3dInterface.o \ $(OBJDIR)/gwf3tvk8.o \ $(OBJDIR)/MemoryManagerExt.o \ $(OBJDIR)/IdmMf6FileLoader.o \ -$(OBJDIR)/gwf3vsc8.o \ $(OBJDIR)/GwfNpfOptions.o \ $(OBJDIR)/CellWithNbrs.o \ $(OBJDIR)/NumericalExchange.o \ @@ -159,8 +159,11 @@ $(OBJDIR)/Iunit.o \ $(OBJDIR)/gwf3uzf8.o \ $(OBJDIR)/gwt1apt1.o \ $(OBJDIR)/GwtSpc.o \ +$(OBJDIR)/gwf3sfr8.o \ $(OBJDIR)/OutputControl.o \ $(OBJDIR)/gwt1ic1.o \ +$(OBJDIR)/gwf3maw8.o \ +$(OBJDIR)/gwf3lak8.o \ $(OBJDIR)/gwt1mst1.o \ $(OBJDIR)/GwtDspOptions.o \ $(OBJDIR)/gwf3npf8.o \ @@ -171,6 +174,7 @@ $(OBJDIR)/Mover.o \ $(OBJDIR)/GwfMvrPeriodData.o \ $(OBJDIR)/ims8misc.o \ $(OBJDIR)/GwfBuyInputData.o \ +$(OBJDIR)/GwfVscInputData.o \ $(OBJDIR)/InterfaceMap.o \ $(OBJDIR)/gwf3disu8.o \ $(OBJDIR)/GridSorting.o \ @@ -196,6 +200,7 @@ $(OBJDIR)/gwf3disv8.o \ $(OBJDIR)/gwf3dis8.o \ $(OBJDIR)/gwf3api8.o \ $(OBJDIR)/gwf3wel8.o \ +$(OBJDIR)/gwf3riv8.o \ $(OBJDIR)/gwf3rch8.o \ $(OBJDIR)/gwf3sto8.o \ $(OBJDIR)/gwf3oc8.o \ @@ -205,7 +210,10 @@ $(OBJDIR)/gwf3hfb8.o \ $(OBJDIR)/gwf3csub8.o \ $(OBJDIR)/gwf3buy8.o \ $(OBJDIR)/GhostNode.o \ +$(OBJDIR)/gwf3ghb8.o \ $(OBJDIR)/gwf3evt8.o \ +$(OBJDIR)/gwf3drn8.o \ +$(OBJDIR)/gwf3vsc8.o \ $(OBJDIR)/gwf3chd8.o \ $(OBJDIR)/ims8reordering.o \ $(OBJDIR)/GridConnection.o \ From fef6b04075214e8935d1ff629aafda52bee5d5dd Mon Sep 17 00:00:00 2001 From: Eric Morway Date: Mon, 31 Oct 2022 09:27:58 -0700 Subject: [PATCH 16/31] Retrying makefile after running build_makefiles.py --- make/makefile | 74 +++++++++++++++++++++++++-------------------------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/make/makefile b/make/makefile index e6ed82d2e9d..2cf7532c530 100644 --- a/make/makefile +++ b/make/makefile @@ -6,28 +6,28 @@ include ./makedefaults # Define the source file directories SOURCEDIR1=../src SOURCEDIR2=../src/Exchange -SOURCEDIR3=../src/Solution -SOURCEDIR4=../src/Solution/LinearMethods -SOURCEDIR5=../src/Timing -SOURCEDIR6=../src/Utilities -SOURCEDIR7=../src/Utilities/Idm -SOURCEDIR8=../src/Utilities/TimeSeries -SOURCEDIR9=../src/Utilities/Memory -SOURCEDIR10=../src/Utilities/OutputControl -SOURCEDIR11=../src/Utilities/ArrayRead -SOURCEDIR12=../src/Utilities/Libraries -SOURCEDIR13=../src/Utilities/Libraries/rcm -SOURCEDIR14=../src/Utilities/Libraries/blas -SOURCEDIR15=../src/Utilities/Libraries/sparskit2 -SOURCEDIR16=../src/Utilities/Libraries/daglib -SOURCEDIR17=../src/Utilities/Libraries/sparsekit -SOURCEDIR18=../src/Utilities/Observation -SOURCEDIR19=../src/Model -SOURCEDIR20=../src/Model/Connection -SOURCEDIR21=../src/Model/GroundWaterTransport -SOURCEDIR22=../src/Model/ModelUtilities -SOURCEDIR23=../src/Model/GroundWaterFlow -SOURCEDIR24=../src/Model/Geometry +SOURCEDIR3=../src/Model +SOURCEDIR4=../src/Model/Connection +SOURCEDIR5=../src/Model/Geometry +SOURCEDIR6=../src/Model/GroundWaterFlow +SOURCEDIR7=../src/Model/GroundWaterTransport +SOURCEDIR8=../src/Model/ModelUtilities +SOURCEDIR9=../src/Solution +SOURCEDIR10=../src/Solution/LinearMethods +SOURCEDIR11=../src/Timing +SOURCEDIR12=../src/Utilities +SOURCEDIR13=../src/Utilities/ArrayRead +SOURCEDIR14=../src/Utilities/Idm +SOURCEDIR15=../src/Utilities/Libraries +SOURCEDIR16=../src/Utilities/Libraries/blas +SOURCEDIR17=../src/Utilities/Libraries/daglib +SOURCEDIR18=../src/Utilities/Libraries/rcm +SOURCEDIR19=../src/Utilities/Libraries/sparsekit +SOURCEDIR20=../src/Utilities/Libraries/sparskit2 +SOURCEDIR21=../src/Utilities/Memory +SOURCEDIR22=../src/Utilities/Observation +SOURCEDIR23=../src/Utilities/OutputControl +SOURCEDIR24=../src/Utilities/TimeSeries VPATH = \ ${SOURCEDIR1} \ @@ -111,47 +111,55 @@ $(OBJDIR)/gwf3disv8idm.o \ $(OBJDIR)/gwf3disu8idm.o \ $(OBJDIR)/gwf3dis8idm.o \ $(OBJDIR)/Integer2dReader.o \ +$(OBJDIR)/BudgetFileReader.o \ $(OBJDIR)/TimeArraySeriesManager.o \ $(OBJDIR)/PackageMover.o \ $(OBJDIR)/Obs3.o \ $(OBJDIR)/NumericalPackage.o \ $(OBJDIR)/Budget.o \ -$(OBJDIR)/BudgetFileReader.o \ $(OBJDIR)/StructVector.o \ $(OBJDIR)/IdmLogger.o \ $(OBJDIR)/InputDefinitionSelector.o \ $(OBJDIR)/Integer1dReader.o \ $(OBJDIR)/Double2dReader.o \ $(OBJDIR)/Double1dReader.o \ +$(OBJDIR)/sort.o \ +$(OBJDIR)/SfrCrossSectionUtils.o \ +$(OBJDIR)/BudgetTerm.o \ $(OBJDIR)/BoundaryPackage.o \ $(OBJDIR)/BaseModel.o \ -$(OBJDIR)/BudgetTerm.o \ $(OBJDIR)/StructArray.o \ $(OBJDIR)/ModflowInput.o \ $(OBJDIR)/LayeredArrayReader.o \ +$(OBJDIR)/SfrCrossSectionManager.o \ +$(OBJDIR)/dag_module.o \ +$(OBJDIR)/BudgetObject.o \ $(OBJDIR)/NumericalModel.o \ $(OBJDIR)/mf6lists.o \ $(OBJDIR)/PackageBudget.o \ $(OBJDIR)/HeadFileReader.o \ -$(OBJDIR)/BudgetObject.o \ -$(OBJDIR)/sort.o \ -$(OBJDIR)/SfrCrossSectionUtils.o \ $(OBJDIR)/PrintSaveManager.o \ $(OBJDIR)/Xt3dAlgorithm.o \ $(OBJDIR)/gwf3tvbase8.o \ $(OBJDIR)/LoadMf6FileType.o \ +$(OBJDIR)/gwf3sfr8.o \ +$(OBJDIR)/gwf3riv8.o \ +$(OBJDIR)/gwf3maw8.o \ +$(OBJDIR)/gwf3lak8.o \ +$(OBJDIR)/GwfVscInputData.o \ +$(OBJDIR)/gwf3ghb8.o \ +$(OBJDIR)/gwf3drn8.o \ $(OBJDIR)/DistributedModel.o \ $(OBJDIR)/BaseExchange.o \ $(OBJDIR)/UzfCellGroup.o \ $(OBJDIR)/gwt1fmi1.o \ -$(OBJDIR)/SfrCrossSectionManager.o \ -$(OBJDIR)/dag_module.o \ $(OBJDIR)/OutputControlData.o \ $(OBJDIR)/gwf3ic8.o \ $(OBJDIR)/Xt3dInterface.o \ $(OBJDIR)/gwf3tvk8.o \ $(OBJDIR)/MemoryManagerExt.o \ $(OBJDIR)/IdmMf6FileLoader.o \ +$(OBJDIR)/gwf3vsc8.o \ $(OBJDIR)/GwfNpfOptions.o \ $(OBJDIR)/CellWithNbrs.o \ $(OBJDIR)/NumericalExchange.o \ @@ -159,11 +167,8 @@ $(OBJDIR)/Iunit.o \ $(OBJDIR)/gwf3uzf8.o \ $(OBJDIR)/gwt1apt1.o \ $(OBJDIR)/GwtSpc.o \ -$(OBJDIR)/gwf3sfr8.o \ $(OBJDIR)/OutputControl.o \ $(OBJDIR)/gwt1ic1.o \ -$(OBJDIR)/gwf3maw8.o \ -$(OBJDIR)/gwf3lak8.o \ $(OBJDIR)/gwt1mst1.o \ $(OBJDIR)/GwtDspOptions.o \ $(OBJDIR)/gwf3npf8.o \ @@ -174,7 +179,6 @@ $(OBJDIR)/Mover.o \ $(OBJDIR)/GwfMvrPeriodData.o \ $(OBJDIR)/ims8misc.o \ $(OBJDIR)/GwfBuyInputData.o \ -$(OBJDIR)/GwfVscInputData.o \ $(OBJDIR)/InterfaceMap.o \ $(OBJDIR)/gwf3disu8.o \ $(OBJDIR)/GridSorting.o \ @@ -200,7 +204,6 @@ $(OBJDIR)/gwf3disv8.o \ $(OBJDIR)/gwf3dis8.o \ $(OBJDIR)/gwf3api8.o \ $(OBJDIR)/gwf3wel8.o \ -$(OBJDIR)/gwf3riv8.o \ $(OBJDIR)/gwf3rch8.o \ $(OBJDIR)/gwf3sto8.o \ $(OBJDIR)/gwf3oc8.o \ @@ -210,10 +213,7 @@ $(OBJDIR)/gwf3hfb8.o \ $(OBJDIR)/gwf3csub8.o \ $(OBJDIR)/gwf3buy8.o \ $(OBJDIR)/GhostNode.o \ -$(OBJDIR)/gwf3ghb8.o \ $(OBJDIR)/gwf3evt8.o \ -$(OBJDIR)/gwf3drn8.o \ -$(OBJDIR)/gwf3vsc8.o \ $(OBJDIR)/gwf3chd8.o \ $(OBJDIR)/ims8reordering.o \ $(OBJDIR)/GridConnection.o \ From fa5be12f2de5a64644a4bc4fec37785fb6458e38 Mon Sep 17 00:00:00 2001 From: Eric Morway Date: Mon, 31 Oct 2022 10:05:47 -0700 Subject: [PATCH 17/31] new autotest includes a data file that should have submitted previously --- .../data/vsc04-laktab/stg-vol-surfarea.dat | 152 ++++++++++++++++++ autotest/test_gwf_vsc04_lak.py | 2 +- 2 files changed, 153 insertions(+), 1 deletion(-) create mode 100644 autotest/data/vsc04-laktab/stg-vol-surfarea.dat diff --git a/autotest/data/vsc04-laktab/stg-vol-surfarea.dat b/autotest/data/vsc04-laktab/stg-vol-surfarea.dat new file mode 100644 index 00000000000..edc44af27e1 --- /dev/null +++ b/autotest/data/vsc04-laktab/stg-vol-surfarea.dat @@ -0,0 +1,152 @@ +stage,Volume,Surf Area +29.5656,0,0 +29.602176,7645.54858,3870.96 +29.638752,15291.09716,7741.92 +29.675328,22936.64574,11612.88 +29.711904,30582.19432,15483.84 +29.74848,38227.7429,19354.8 +29.785056,45873.29148,23225.76 +29.821632,53518.84006,27096.72 +29.858208,61164.38864,30967.68 +29.894784,68809.93722,34838.64 +29.93136,76455.4858,38709.6 +29.967936,84101.03438,42580.56 +30.004512,91746.58296,46451.52 +30.041088,99392.13154,50322.48 +30.077664,107037.6801,54193.44 +30.11424,114683.2287,58064.4 +30.150816,122328.7773,61935.36 +30.187392,129974.3259,65806.32 +30.223968,137619.8744,69677.28 +30.260544,145265.423,73548.24 +30.29712,152910.9716,77419.2 +30.333696,160556.5202,81290.16 +30.370272,168202.0688,85161.12 +30.406848,175847.6173,89032.08 +30.443424,183493.1659,92903.04 +30.48,191138.7145,96774 +30.516576,198784.2631,100644.96 +30.553152,206429.8117,104515.92 +30.589728,214075.3602,108386.88 +30.626304,221720.9088,112257.84 +30.66288,229366.4574,116128.8 +30.699456,237012.006,119999.76 +30.736032,244657.5546,123870.72 +30.772608,252303.1031,127741.68 +30.809184,259948.6517,131612.64 +30.84576,267594.2003,135483.6 +30.882336,275239.7489,139354.56 +30.918912,282885.2975,143225.52 +30.955488,290530.846,147096.48 +30.992064,298176.3946,150967.44 +31.02864,305821.9432,154838.4 +31.065216,313467.4918,158709.36 +31.101792,321113.0404,162580.32 +31.138368,328758.5889,166451.28 +31.174944,336404.1375,170322.24 +31.21152,344049.6861,174193.2 +31.248096,351695.2347,178064.16 +31.284672,359340.7833,181935.12 +31.321248,366986.3318,185806.08 +31.357824,374631.8804,189677.04 +31.3944,382277.429,193548 +31.430976,389922.9776,197418.96 +31.467552,397568.5262,201289.92 +31.504128,405214.0747,205160.88 +31.540704,412859.6233,209031.84 +31.57728,420505.1719,212902.8 +31.613856,428150.7205,216773.76 +31.650432,435796.2691,220644.72 +31.687008,443441.8176,224515.68 +31.723584,451087.3662,228386.64 +31.76016,458732.9148,232257.6 +31.796736,466378.4634,236128.56 +31.833312,474024.012,239999.52 +31.869888,481669.5605,243870.48 +31.906464,489315.1091,247741.44 +31.94304,496960.6577,251612.4 +31.979616,504606.2063,255483.36 +32.016192,512251.7548,259354.32 +32.052768,519897.3034,263225.28 +32.089344,527542.852,267096.24 +32.12592,535188.4006,270967.2 +32.162496,542833.9492,274838.16 +32.199072,550479.4977,278709.12 +32.235648,558125.0463,282580.08 +32.272224,565770.5949,286451.04 +32.3088,573416.1435,290322 +32.345376,581061.6921,294192.96 +32.381952,588707.2406,298063.92 +32.418528,596352.7892,301934.88 +32.455104,603998.3378,305805.84 +32.49168,611643.8864,309676.8 +32.528256,619289.435,313547.76 +32.564832,626934.9835,317418.72 +32.601408,634580.5321,321289.68 +32.637984,651287.4716,325160.64 +32.67456,672525.1066,329031.6 +32.711136,693762.7415,332902.56 +32.747712,715000.3764,336773.52 +32.784288,736238.0114,340644.48 +32.820864,757475.6463,344515.44 +32.85744,778713.2813,348386.4 +32.894016,799950.9162,352257.36 +32.930592,821188.5512,356128.32 +32.967168,842426.1861,359999.28 +33.003744,863663.8211,363870.24 +33.04032,884901.456,367741.2 +33.076896,906139.0909,371612.16 +33.113472,927376.7259,375483.12 +33.150048,948614.3608,379354.08 +33.186624,969851.9958,383225.04 +33.2232,991089.6307,387096 +33.259776,1012327.266,390966.96 +33.296352,1033564.901,394837.92 +33.332928,1054802.536,398708.88 +33.369504,1076040.17,402579.84 +33.40608,1097277.805,406450.8 +33.442656,1118515.44,410321.76 +33.479232,1139753.075,414192.72 +33.515808,1160990.71,418063.68 +33.552384,1182228.345,421934.64 +33.58896,1203465.98,425805.6 +33.625536,1224703.615,429676.56 +33.662112,1245941.25,433547.52 +33.698688,1267178.885,437418.48 +33.735264,1288416.52,441289.44 +33.77184,1309654.155,445160.4 +33.808416,1330891.79,449031.36 +33.844992,1352129.425,452902.32 +33.881568,1373367.06,456773.28 +33.918144,1394604.695,460644.24 +33.95472,1415842.33,464515.2 +33.991296,1437079.965,468386.16 +34.027872,1458317.599,472257.12 +34.064448,1479555.234,476128.08 +34.101024,1500792.869,479999.04 +34.1376,1522030.504,483870 +34.174176,1543268.139,487740.96 +34.210752,1564505.774,491611.92 +34.247328,1585743.409,495482.88 +34.283904,1606981.044,499353.84 +34.32048,1628218.679,503224.8 +34.357056,1649456.314,507095.76 +34.393632,1670693.949,510966.72 +34.430208,1691931.584,514837.68 +34.466784,1713169.219,518708.64 +34.50336,1734406.854,522579.6 +34.539936,1755644.489,526450.56 +34.576512,1776882.124,530321.52 +34.613088,1798119.759,534192.48 +34.649664,1819357.394,538063.44 +34.68624,1840595.028,541934.4 +34.722816,1861832.663,545805.36 +34.759392,1883070.298,549676.32 +34.795968,1904307.933,553547.28 +34.832544,1925545.568,557418.24 +34.86912,1946783.203,561289.2 +34.905696,1968020.838,565160.16 +34.942272,1989258.473,569031.12 +34.978848,2010496.108,572902.08 +35.015424,2031733.743,576773.04 +35.052,2052971.378,580644 diff --git a/autotest/test_gwf_vsc04_lak.py b/autotest/test_gwf_vsc04_lak.py index 318c7385305..5706b29960c 100644 --- a/autotest/test_gwf_vsc04_lak.py +++ b/autotest/test_gwf_vsc04_lak.py @@ -472,7 +472,7 @@ def build_model(idx, dir): ) # pull in the tabfile defining the lake stage, vol, & surface area - fname = os.path.join("data", "vsc04-laktab", "stg-vol-surfarea.csv") + fname = os.path.join("data", "vsc04-laktab", "stg-vol-surfarea.dat") tabinput = [] with open(fname, "r") as f: # peel off the hdr line From ddb08318ee88ffe70e5504de311e247da9afc87a Mon Sep 17 00:00:00 2001 From: Eric Morway Date: Mon, 31 Oct 2022 12:40:46 -0700 Subject: [PATCH 18/31] Clean-up unused variables & hiccup fix --- autotest/test_gwf_returncodes.py | 2 +- src/Model/GroundWaterFlow/gwf3drn8.f90 | 7 +++---- src/Model/GroundWaterFlow/gwf3ghb8.f90 | 5 ++--- src/Model/GroundWaterFlow/gwf3riv8.f90 | 7 +++---- 4 files changed, 9 insertions(+), 12 deletions(-) diff --git a/autotest/test_gwf_returncodes.py b/autotest/test_gwf_returncodes.py index daea5cec788..9886798e0b8 100644 --- a/autotest/test_gwf_returncodes.py +++ b/autotest/test_gwf_returncodes.py @@ -19,7 +19,7 @@ name = "gwf_ret_codes01" base_ws = os.path.join("temp", name) if not os.path.isdir(base_ws): - os.makedirs(base_ws) + os.makedirs(base_ws, exist_ok=True) app = "mf6" if sys.platform.lower() == "win32": app += ".exe" diff --git a/src/Model/GroundWaterFlow/gwf3drn8.f90 b/src/Model/GroundWaterFlow/gwf3drn8.f90 index 65013261dec..de2363a7679 100644 --- a/src/Model/GroundWaterFlow/gwf3drn8.f90 +++ b/src/Model/GroundWaterFlow/gwf3drn8.f90 @@ -1,9 +1,8 @@ module DrnModule - use KindModule, only: DP, I4B, LGP + use KindModule, only: DP, I4B use ConstantsModule, only: DZERO, DONE, DTWO, LENFTYPE, LENPACKAGENAME, & - LENAUXNAME, LINELENGTH, LENMEMSEPARATOR - use MemoryHelperModule, only: create_mem_path, split_mem_address, & - memPathSeparator + LENAUXNAME, LINELENGTH + use MemoryHelperModule, only: create_mem_path use SmoothingModule, only: sQSaturation, sQSaturationDerivative, & sQuadraticSaturation use BndModule, only: BndType diff --git a/src/Model/GroundWaterFlow/gwf3ghb8.f90 b/src/Model/GroundWaterFlow/gwf3ghb8.f90 index d81b45c1a7e..ddd876fe213 100644 --- a/src/Model/GroundWaterFlow/gwf3ghb8.f90 +++ b/src/Model/GroundWaterFlow/gwf3ghb8.f90 @@ -1,8 +1,7 @@ module ghbmodule - use KindModule, only: DP, I4B, LGP + use KindModule, only: DP, I4B use ConstantsModule, only: DZERO, LENFTYPE, LENPACKAGENAME, LENMEMSEPARATOR - use MemoryHelperModule, only: create_mem_path, split_mem_address, & - memPathSeparator + use MemoryHelperModule, only: create_mem_path use BndModule, only: BndType use ObsModule, only: DefaultObsIdProcessor use TimeSeriesLinkModule, only: TimeSeriesLinkType, & diff --git a/src/Model/GroundWaterFlow/gwf3riv8.f90 b/src/Model/GroundWaterFlow/gwf3riv8.f90 index 825eeb8dac0..f14e93fa2f7 100644 --- a/src/Model/GroundWaterFlow/gwf3riv8.f90 +++ b/src/Model/GroundWaterFlow/gwf3riv8.f90 @@ -1,8 +1,7 @@ module rivmodule - use KindModule, only: DP, I4B, LGP - use ConstantsModule, only: DZERO, LENFTYPE, LENPACKAGENAME, LENMEMSEPARATOR - use MemoryHelperModule, only: create_mem_path, split_mem_address, & - memPathSeparator + use KindModule, only: DP, I4B + use ConstantsModule, only: DZERO, LENFTYPE, LENPACKAGENAME + use MemoryHelperModule, only: create_mem_path use BndModule, only: BndType use ObsModule, only: DefaultObsIdProcessor use TimeSeriesLinkModule, only: TimeSeriesLinkType, & From e097e0ccb4d985d3edb5454ec0bf861e83121091 Mon Sep 17 00:00:00 2001 From: Eric Morway Date: Thu, 3 Nov 2022 04:59:10 -0700 Subject: [PATCH 19/31] - Code now accounts for viscosity within HFB - This commit addresses recent review comments - Adds a new autotest for HFB with and without VSC active --- autotest/test_gwf_vsc05_hfb.py | 431 ++++++++++++++++++++ doc/ReleaseNotes/ReleaseNotes.tex | 2 +- doc/SuppTechInfo/python/VSC-nonlinear.ipynb | 148 +++---- src/Model/GroundWaterFlow/gwf3.f90 | 7 +- src/Model/GroundWaterFlow/gwf3hfb8.f90 | 199 ++++++--- src/Model/GroundWaterFlow/gwf3npf8.f90 | 6 +- src/Model/GroundWaterFlow/gwf3vsc8.f90 | 28 +- 7 files changed, 664 insertions(+), 157 deletions(-) create mode 100644 autotest/test_gwf_vsc05_hfb.py diff --git a/autotest/test_gwf_vsc05_hfb.py b/autotest/test_gwf_vsc05_hfb.py new file mode 100644 index 00000000000..db3d94600f0 --- /dev/null +++ b/autotest/test_gwf_vsc05_hfb.py @@ -0,0 +1,431 @@ +# ## Test problem for VSC and HFB +# +# Uses constant head and general-head boundaries on the left and right +# sides of a 10 row by 10 column by 1 layer model to drive flow from left to +# right. Tests that a horizontal flow barrier accounts for changes in +# viscosity when temperature is simulated. Barrier is between middle two +# columns, but only cuts across the bottom 5 rows. +# Model 1: VSC inactive, uses a higher speified K that matches what the VSC +# package will come up with +# Model 2: VSC active, uses a lower K so that when VSC is applied, resulting +# K's match model 1 and should result in the same flows across the +# model domain +# Model 3: VSC inactive, uses the lower K of model 2 and checks that flows +# in model 3 are indeed lower than in model 2 when turning VSC off. +# Model simulates hot groundwater with lower viscosity resulting in +# more gw flow through the model domain.Flows that are checked are +# the row-wise flows between columns 5 and 6 (e.g., cell 5 to 6, 15 +# to 16, etc.) +# + +# Imports + +import os +import sys + +import numpy as np +import pytest + +try: + import flopy +except: + msg = "Error. FloPy package is not available.\n" + msg += "Try installing using the following command:\n" + msg += " pip install flopy" + raise Exception(msg) + +from framework import testing_framework +from simulation import Simulation + +hyd_cond = [1205.49396942506, 864.0] # Hydraulic conductivity (m/d) +ex = ["no-vsc05-hfb", "vsc05-hfb", "no-vsc05-k"] +viscosity_on = [False, True, False] +hydraulic_conductivity = [hyd_cond[0], hyd_cond[1], hyd_cond[1]] +exdirs = [] +for s in ex: + exdirs.append(os.path.join("temp", s)) + +# Model units + +length_units = "cm" +time_units = "seconds" + +# Table of model parameters + +nper = 1 # Number of periods +nstp = 10 # Number of time steps +perlen = 10 # Simulation time length ($d$) +nlay = 1 # Number of layers +nrow = 10 # Number of rows +ncol = 10 # Number of columns +delr = 1.0 # Column width ($m$) +delc = 1.0 # Row width ($m$) +delv = 1.0 # Layer thickness +top = 1.0 # Top of the model ($m$) +initial_temperature = 35.0 # Initial temperature (unitless) +porosity = 0.26 # porosity (unitless) +K_therm = 2.0 # Thermal conductivity # ($W/m/C$) +rho_water = 1000 # Density of water ($kg/m^3$) +rho_solids = 2650 # Density of the aquifer material ($kg/m^3$) +C_p_w = 4180 # Heat Capacity of water ($J/kg/C$) +C_s = 880 # Heat capacity of the solids ($J/kg/C$) +D_m = K_therm / (porosity * rho_water * C_p_w) +rhob = (1 - porosity) * rho_solids # Bulk density ($kg/m^3$) +K_d = C_s / (rho_water * C_p_w) # Partitioning coefficient ($m^3/kg$) +inflow = 5.7024 # ($m^3/d$) + +botm = [top - k * delv for k in range(1, nlay + 1)] + +nouter, ninner = 100, 300 +hclose, rclose, relax = 1e-10, 1e-6, 0.97 + +# +# MODFLOW 6 flopy GWF simulation object (sim) is returned +# + + +def build_model(idx, dir): + # Base simulation and model name and workspace + ws = dir + name = ex[idx] + + print("Building model...{}".format(name)) + + # generate names for each model + gwfname = "gwf-" + name + gwtname = "gwt-" + name + + sim = flopy.mf6.MFSimulation( + sim_name=name, version="mf6", exe_name="mf6", sim_ws=ws + ) + + # Instantiating time discretization + tdis_ds = ((perlen, nstp, 1.0),) + flopy.mf6.ModflowTdis( + sim, nper=nper, perioddata=tdis_ds, time_units=time_units + ) + gwf = flopy.mf6.ModflowGwf(sim, modelname=gwfname, save_flows=True) + + # Instantiating solver + ims = flopy.mf6.ModflowIms( + sim, + print_option="ALL", + outer_dvclose=hclose, + outer_maximum=nouter, + under_relaxation="NONE", + inner_maximum=ninner, + inner_dvclose=hclose, + rcloserecord=rclose, + linear_acceleration="BICGSTAB", + scaling_method="NONE", + reordering_method="NONE", + relaxation_factor=relax, + filename="{}.ims".format(gwfname), + ) + sim.register_ims_package(ims, [gwfname]) + + # Instantiating DIS + flopy.mf6.ModflowGwfdis( + gwf, + length_units=length_units, + nlay=nlay, + nrow=nrow, + ncol=ncol, + delr=delr, + delc=delc, + top=top, + botm=botm, + ) + + # Instantiating NPF + flopy.mf6.ModflowGwfnpf( + gwf, + save_specific_discharge=True, + icelltype=0, + k=hydraulic_conductivity[idx], + ) + flopy.mf6.ModflowGwfic(gwf, strt=0.0) + + # Instantiating VSC + if viscosity_on[idx]: + # Instantiate viscosity (VSC) package + vsc_filerecord = "{}.vsc.bin".format(gwfname) + vsc_pd = [(0, 0.0, 20.0, gwtname, "temperature")] + flopy.mf6.ModflowGwfvsc( + gwf, + viscref=8.904e-4, + viscosity_filerecord=vsc_filerecord, + viscosityfuncrecord=[("nonlinear", 10.0, 248.37, 133.16)], + nviscspecies=len(vsc_pd), + packagedata=vsc_pd, + pname="vsc", + filename="{}.vsc".format(gwfname), + ) + + # Instantiating CHD (leftside, "inflow" boundary) + chdspd = [[(0, i, 0), 2.0, initial_temperature] for i in range(nrow)] + flopy.mf6.ModflowGwfchd( + gwf, + stress_period_data=chdspd, + pname="CHD-1", + auxiliary="temperature", + ) + + # Instantiating GHB (rightside, "outflow" boundary) + ghbcond = hydraulic_conductivity[idx] * delv * delc / (0.5 * delr) + ghbspd = [ + [(0, i, ncol - 1), top, ghbcond, initial_temperature] + for i in range(nrow) + ] + flopy.mf6.ModflowGwfghb( + gwf, + stress_period_data=ghbspd, + pname="GHB-1", + auxiliary="temperature", + ) + + # Instantiate Horizontal Flow-Barrier (HFB) package + # Barrier present between middle two columns of the model domain, but only + # in rows 6-10. Remember that the hydraulic characteristic is the barrier + # hydraulic conductivity divided by the width of the horizontal-flow + # barrier. Assuming a barrier width of 10 cm (0.1 m) and desire to have + # the barrier's K be 1/10th of the aquifer hydraulic conductivity. + hfbspd = [] + K = 0.1 * hydraulic_conductivity[idx] + for i in np.arange(5, 10, 1): + hfbspd.append(((0, i, 4), (0, i, 5), K)) + flopy.mf6.ModflowGwfhfb( + gwf, + print_input=True, + maxhfb=len(hfbspd), + stress_period_data=hfbspd, + pname="HFB-1", + ) + + # Instatiating OC + head_filerecord = "{}.hds".format(gwfname) + budget_filerecord = "{}.bud".format(gwfname) + flopy.mf6.ModflowGwfoc( + gwf, + head_filerecord=head_filerecord, + budget_filerecord=budget_filerecord, + saverecord=[("HEAD", "ALL"), ("BUDGET", "ALL")], + ) + + # Setup the GWT model for simulating heat transport + # ------------------------------------------------- + gwt = flopy.mf6.ModflowGwt(sim, modelname=gwtname) + + # Instantiating solver for GWT + imsgwt = flopy.mf6.ModflowIms( + sim, + print_option="ALL", + outer_dvclose=hclose, + outer_maximum=nouter, + under_relaxation="NONE", + inner_maximum=ninner, + inner_dvclose=hclose, + rcloserecord=rclose, + linear_acceleration="BICGSTAB", + scaling_method="NONE", + reordering_method="NONE", + relaxation_factor=relax, + filename="{}.ims".format(gwtname), + ) + sim.register_ims_package(imsgwt, [gwtname]) + + # Instantiating DIS for GWT + flopy.mf6.ModflowGwtdis( + gwt, + length_units=length_units, + nlay=nlay, + nrow=nrow, + ncol=ncol, + delr=delr, + delc=delc, + top=top, + botm=botm, + ) + + # Instantiating MST for GWT + flopy.mf6.ModflowGwtmst( + gwt, + porosity=porosity, + sorption="linear", + bulk_density=rhob, + distcoef=K_d, + pname="MST-1", + filename="{}.mst".format(gwtname), + ) + + # Instantiating IC for GWT + flopy.mf6.ModflowGwtic(gwt, strt=initial_temperature) + + # Instantiating ADV for GWT + flopy.mf6.ModflowGwtadv(gwt, scheme="UPSTREAM") + + # Instantiating DSP for GWT + flopy.mf6.ModflowGwtdsp(gwt, xt3d_off=True, diffc=D_m) + + # Instantiating SSM for GWT + sourcerecarray = [ + ("CHD-1", "AUX", "TEMPERATURE"), + ("GHB-1", "AUX", "TEMPERATURE"), + ] + flopy.mf6.ModflowGwtssm(gwt, sources=sourcerecarray) + + # Instantiating OC for GWT + flopy.mf6.ModflowGwtoc( + gwt, + concentration_filerecord="{}.ucn".format(gwtname), + saverecord=[("CONCENTRATION", "ALL")], + printrecord=[("CONCENTRATION", "LAST"), ("BUDGET", "LAST")], + ) + + # Instantiating GWF/GWT Exchange + flopy.mf6.ModflowGwfgwt( + sim, exgtype="GWF6-GWT6", exgmnamea=gwfname, exgmnameb=gwtname + ) + + return sim, None + + +def eval_results(sim): + print("evaluating results...") + + # read flow results from model + name = ex[sim.idxsim] + gwfname = "gwf-" + name + sim1 = flopy.mf6.MFSimulation.load(sim_ws=sim.simpath, load_only=["dis"]) + gwf = sim1.get_model(gwfname) + + # Get grid data + grdname = gwfname + ".dis.grb" + bgf = flopy.mf6.utils.MfGrdFile(os.path.join(sim.simpath, grdname)) + ia, ja = bgf.ia, bgf.ja + + fname = gwfname + ".bud" + fname = os.path.join(sim.simpath, fname) + assert os.path.isfile(fname) + budobj = flopy.utils.CellBudgetFile(fname, precision="double") + outbud = budobj.get_data(text=" FLOW-JA-FACE")[-1].squeeze() + + # Look at flow entering the left face for the cells in the 6th (1-based) column + cells = [gwf.modelgrid.get_node([(0, i, 5)])[0] for i in np.arange(nrow)] + + vals_to_store = [] # Will always have 10 vals, 1 per row + + # Note that the layer, row, column indices will be zero-based + for celln in cells: + for ipos in range(ia[celln] + 1, ia[celln + 1]): + cellm = ja[ipos] + if cellm == celln - 1: + vals_to_store.append([cellm, celln, outbud[ipos]]) + + if sim.idxsim == 0: + no_vsc_bud_last = np.array(vals_to_store) + np.savetxt( + os.path.join(os.path.dirname(exdirs[sim.idxsim]), "mod1reslt.txt"), + no_vsc_bud_last, + ) + + elif sim.idxsim == 1: + with_vsc_bud_last = np.array(vals_to_store) + np.savetxt( + os.path.join(os.path.dirname(exdirs[sim.idxsim]), "mod2reslt.txt"), + with_vsc_bud_last, + ) + + elif sim.idxsim == 2: + no_vsc_low_k_bud_last = np.array(vals_to_store) + np.savetxt( + os.path.join(os.path.dirname(exdirs[sim.idxsim]), "mod3reslt.txt"), + no_vsc_low_k_bud_last, + ) + + elif sim.idxsim == 3: + with_vscoff_bud_last = np.array(vals_to_store) + np.savetxt( + os.path.join(os.path.dirname(exdirs[sim.idxsim]), "mod4reslt.txt"), + with_vscoff_bud_last, + ) + + # if all 4 models have run, check relative results + if sim.idxsim == 2: + f1 = os.path.join(os.path.dirname(exdirs[sim.idxsim]), "mod1reslt.txt") + if os.path.isfile(f1): + no_vsc_bud_last = np.loadtxt(f1) + os.remove(f1) + + f2 = os.path.join(os.path.dirname(exdirs[sim.idxsim]), "mod2reslt.txt") + if os.path.isfile(f2): + with_vsc_bud_last = np.loadtxt(f2) + os.remove(f2) + + f3 = os.path.join(os.path.dirname(exdirs[sim.idxsim]), "mod3reslt.txt") + if os.path.isfile(f3): + no_vsc_low_k_bud_last = np.loadtxt(f3) + os.remove(f3) + + # model1_exit = no_vsc_bud_last[:, 2].sum() + # model2_exit = with_vsc_bud_last[:, 2].sum() + # model3_exit = no_vsc_low_k_bud_last[:, 2].sum() + + # Ensure models 1 & 2 give nearly identical flow results + # for each cell-to-cell exchange between columns 5 and 6 + assert np.allclose( + no_vsc_bud_last[:, 2], with_vsc_bud_last[:, 2], atol=1e-3 + ), ( + "Flow in models " + + exdirs[0] + + " and " + + exdirs[1] + + " should be approximately equal, but are not." + ) + + # Ensure the cell-to-cell flow between columns 5 and 6 in model + # 3 is less than what's in model 2 + assert np.less( + no_vsc_low_k_bud_last[:, 2], with_vsc_bud_last[:, 2] + ).all(), ( + "Exit flow from model " + + exdirs[1] + + " should be greater than flow existing " + + exdirs[2] + + ", but it is not." + ) + + +# - No need to change any code below +@pytest.mark.parametrize( + "idx, dir", + list(enumerate(exdirs)), +) +def test_mf6model(idx, dir): + # initialize testing framework + test = testing_framework() + + # build the model + test.build_mf6_models(build_model, idx, dir) + + # run the test model + test.run_mf6(Simulation(dir, exfunc=eval_results, idxsim=idx)) + + +def main(): + # initialize testing framework + test = testing_framework() + + # run the test model + for idx, dir in enumerate(exdirs): + test.build_mf6_models(build_model, idx, dir) + sim = Simulation(dir, exfunc=eval_results, idxsim=idx) + test.run_mf6(sim) + + +if __name__ == "__main__": + # print message + print(f"standalone run of {os.path.basename(__file__)}") + + # run main routine + main() diff --git a/doc/ReleaseNotes/ReleaseNotes.tex b/doc/ReleaseNotes/ReleaseNotes.tex index 5aefe2555b4..67d6c430c94 100644 --- a/doc/ReleaseNotes/ReleaseNotes.tex +++ b/doc/ReleaseNotes/ReleaseNotes.tex @@ -192,7 +192,7 @@ \section{Changes Introduced in this Release} \underline{NEW FUNCTIONALITY} \begin{itemize} - \item A new Viscosity (VSC) package for the Groundwater Flow (GWF) Model is introduced in this release. The effects of viscosity are accounted for by updates intercell conductance, as well as the conductance between the aquifer and head-dependent boundaries, based on simulated concentrations and\/or temperatures. The viscosity package is activated by specifying ``VSC6'' as the file type in a GWF name file. Testing of the VSC Package is accomplished using new autotests; however, changes to the code and input may be required in response to user needs and testing. + \item A new Viscosity (VSC) package for the Groundwater Flow (GWF) Model is introduced in this release. The effects of viscosity are accounted for by updates to intercell conductance, as well as the conductance between the aquifer and head-dependent boundaries, based on simulated concentrations and\/or temperatures. The viscosity package is activated by specifying ``VSC6'' as the file type in a GWF name file. Changes to the code and input may be required in response to user needs and testing. % \item xxx % \item xxx \end{itemize} diff --git a/doc/SuppTechInfo/python/VSC-nonlinear.ipynb b/doc/SuppTechInfo/python/VSC-nonlinear.ipynb index 2bae3ae391c..c907b5687f0 100644 --- a/doc/SuppTechInfo/python/VSC-nonlinear.ipynb +++ b/doc/SuppTechInfo/python/VSC-nonlinear.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 81, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -15,7 +15,7 @@ }, { "cell_type": "code", - "execution_count": 82, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -28,7 +28,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -48,7 +48,7 @@ }, { "cell_type": "code", - "execution_count": 119, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -60,7 +60,7 @@ }, { "cell_type": "code", - "execution_count": 120, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -88,7 +88,7 @@ }, { "cell_type": "code", - "execution_count": 126, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -116,7 +116,7 @@ }, { "cell_type": "code", - "execution_count": 127, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -126,32 +126,9 @@ }, { "cell_type": "code", - "execution_count": 128, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "[]" - ] - }, - "execution_count": 128, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "plt.plot(T,visc)\n", "plt.plot(T,visc_lin)" @@ -166,18 +143,9 @@ }, { "cell_type": "code", - "execution_count": 124, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[ 0 5 10 15 20 25 30 35 40]\n", - "[0.0006 0.0008 0.001 0.0012 0.0014 0.0016 0.0018 0.002 ]\n" - ] - } - ], + "outputs": [], "source": [ "x_increment = 5\n", "x_ticks = np.arange(T0, T1 + x_increment, x_increment)\n", @@ -189,74 +157,50 @@ }, { "cell_type": "code", - "execution_count": 154, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "ename": "AttributeError", - "evalue": "'NoneType' object has no attribute 'add_text'", - "output_type": "error", - "traceback": [ - "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[1;31mAttributeError\u001b[0m Traceback (most recent call last)", - "\u001b[1;32m\u001b[0m in \u001b[0;36m\u001b[1;34m\u001b[0m\n\u001b[0;32m 27\u001b[0m \u001b[0mtext\u001b[0m \u001b[1;33m=\u001b[0m \u001b[1;34mr'$\\left ( T_0, \\mu_0 \\right )$'\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 28\u001b[0m \u001b[1;31m#ax.text(21, 0.00102, text, style='italic') #fontsize=6)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 29\u001b[1;33m \u001b[0mspnspecs\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0madd_text\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0max\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mtext\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mtext\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mx\u001b[0m\u001b[1;33m=\u001b[0m\u001b[1;36m0.5\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0my\u001b[0m\u001b[1;33m=\u001b[0m\u001b[1;36m0.5\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mtransform\u001b[0m\u001b[1;33m=\u001b[0m\u001b[1;32mFalse\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mbold\u001b[0m\u001b[1;33m=\u001b[0m\u001b[1;32mFalse\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mha\u001b[0m\u001b[1;33m=\u001b[0m\u001b[1;34m'left'\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mva\u001b[0m\u001b[1;33m=\u001b[0m\u001b[1;34m'top'\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 30\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 31\u001b[0m \u001b[1;32mif\u001b[0m \u001b[0mspnspecs\u001b[0m \u001b[1;32mis\u001b[0m \u001b[1;32mnot\u001b[0m \u001b[1;32mNone\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", - "\u001b[1;31mAttributeError\u001b[0m: 'NoneType' object has no attribute 'add_text'" - ] - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ - "fig, axes = plt.subplots(nrows=1, ncols=1, tight_layout=True, \n", - " figsize=(width/1.5, width/2))\n", + "#fig, axes = plt.subplots(nrows=1, ncols=1, tight_layout=True, \n", + "# figsize=(width/1.5, width/2))\n", "\n", - "if not isinstance(axes, list):\n", - " axes = [axes]\n", + "#if not isinstance(axes, list):\n", + "# axes = [axes]\n", "\n", - "for idx, ax in enumerate(axes):\n", - " ax.set_xlim(T0, T1)\n", - " ax.set_ylim(y_ticks[0], y_ticks[-2])\n", - " ax.set_xticks(x_ticks)\n", - " ax.set_xticklabels(x_ticks)\n", - " #ax.set_yticks(y_ticks)\n", - " #ax.set_yticklabels(y_ticks, rotation=90, va='center')\n", - " if spnspecs is not None:\n", - " spnspecs.remove_edge_ticks(ax)\n", - " spnspecs.heading(ax, letter=letters[idx])\n", + "#for idx, ax in enumerate(axes):\n", + "# ax.set_xlim(T0, T1)\n", + "# ax.set_ylim(y_ticks[0], y_ticks[-2])\n", + "# ax.set_xticks(x_ticks)\n", + "# ax.set_xticklabels(x_ticks)\n", + "# #ax.set_yticks(y_ticks)\n", + "# #ax.set_yticklabels(y_ticks, rotation=90, va='center')\n", + "# if spnspecs is not None:\n", + "# spnspecs.remove_edge_ticks(ax)\n", + "# spnspecs.heading(ax, letter=letters[idx])\n", " \n", "\n", - "ax = axes[0]\n", - "#ax.set_aspect('square', adjustable='box')\n", - "#ax.axhline(0, lw=0.5, ls='-.', color='black')\n", - "#ax.axvline(0, lw=0.5, ls='-.', color='black')\n", + "#ax = axes[0]\n", + "##ax.set_aspect('square', adjustable='box')\n", + "##ax.axhline(0, lw=0.5, ls='-.', color='black')\n", + "##ax.axvline(0, lw=0.5, ls='-.', color='black')\n", "\n", - "ax.plot(T, visc, lw=1.25, color='#00BFFF', label=r'$\\mu')\n", - "ax.plot(T, visc_lin, lw=1., color='black', ls='-', label=r'$\\frac {\\partial \\mu} {\\partial T}')\n", - "ax.plot([Tviscref], [mu_0], marker=\"o\", markersize=4, markeredgecolor=\"red\", markerfacecolor=\"blue\")\n", - "text = r'$\\left ( T_0, \\mu_0 \\right )$'\n", - "#ax.text(21, 0.00102, text, style='italic') #fontsize=6)\n", - "spnspecs.add_text(ax, text=text, x=0.5, y=0.5, transform=False, bold=False, ha='left', va='top')\n", + "#ax.plot(T, visc, lw=1.25, color='#00BFFF', label=r'$\\mu')\n", + "#ax.plot(T, visc_lin, lw=1., color='black', ls='-', label=r'$\\frac {\\partial \\mu} {\\partial T}')\n", + "#ax.plot([Tviscref], [mu_0], marker=\"o\", markersize=4, markeredgecolor=\"red\", markerfacecolor=\"blue\")\n", + "#text = r'$\\left ( T_0, \\mu_0 \\right )$'\n", + "##ax.text(21, 0.00102, text, style='italic') #fontsize=6)\n", + "#spnspecs.add_text(ax, text=text, x=0.5, y=0.5, transform=False, bold=False, ha='left', va='top')\n", "\n", - "if spnspecs is not None:\n", - " handles, labels = ax.get_legend_handles_labels()\n", - " spnspecs.graph_legend(ax, handles=handles[::-1], labels=labels[::-1],\n", - " loc='upper right', bbox_to_anchor=(1.00,0.95),\n", - " handlelength=1.5)\n", - "ax.set_xlabel(r'Temperature, in $^\\circ$C')\n", - "ax.set_ylabel(r'$\\mu$, in $\\frac{kg}{m \\cdot s}$')\n", + "#if spnspecs is not None:\n", + "# handles, labels = ax.get_legend_handles_labels()\n", + "# spnspecs.graph_legend(ax, handles=handles[::-1], labels=labels[::-1],\n", + "# loc='upper right', bbox_to_anchor=(1.00,0.95),\n", + "# handlelength=1.5)\n", + "#ax.set_xlabel(r'Temperature, in $^\\circ$C')\n", + "#ax.set_ylabel(r'$\\mu$, in $\\frac{kg}{m \\cdot s}$')\n", "\n", - "fpth = os.path.join(figpth, 'VSCnonlinear.pdf')\n", - "fig.savefig(fpth, dpi=dpi)" + "#fpth = os.path.join(figpth, 'VSCnonlinear.pdf')\n", + "#fig.savefig(fpth, dpi=dpi)" ] }, { diff --git a/src/Model/GroundWaterFlow/gwf3.f90 b/src/Model/GroundWaterFlow/gwf3.f90 index 2bb74273aa3..e9963e0336d 100644 --- a/src/Model/GroundWaterFlow/gwf3.f90 +++ b/src/Model/GroundWaterFlow/gwf3.f90 @@ -427,7 +427,8 @@ subroutine gwf_ar(this) this%x) if (this%invsc > 0) call this%vsc%vsc_ar(this%ibound) if (this%inbuy > 0) call this%buy%buy_ar(this%npf, this%ibound) - if (this%inhfb > 0) call this%hfb%hfb_ar(this%ibound, this%xt3d, this%dis) + if (this%inhfb > 0) call this%hfb%hfb_ar(this%ibound, this%xt3d, this%dis, & + this%invsc, this%vsc, this%x) if (this%insto > 0) call this%sto%sto_ar(this%dis, this%ibound) if (this%incsub > 0) call this%csub%csub_ar(this%dis, this%ibound) if (this%inmvr > 0) call this%mvr%mvr_ar() @@ -604,7 +605,7 @@ subroutine gwf_fc(this, kiter, amatsln, njasln, inwtflag) if (this%inbuy > 0) call this%buy%buy_fc(kiter, njasln, amatsln, & this%idxglo, this%rhs, this%x) if (this%inhfb > 0) call this%hfb%hfb_fc(kiter, njasln, amatsln, & - this%idxglo, this%rhs, this%x) + this%idxglo, this%rhs) if (this%ingnc > 0) call this%gnc%gnc_fc(kiter, amatsln) ! -- storage if (this%insto > 0) then @@ -925,7 +926,7 @@ subroutine gwf_cq(this, icnvg, isuppress_output) end do if (this%innpf > 0) call this%npf%npf_cq(this%x, this%flowja) if (this%inbuy > 0) call this%buy%buy_cq(this%x, this%flowja) - if (this%inhfb > 0) call this%hfb%hfb_cq(this%x, this%flowja) + if (this%inhfb > 0) call this%hfb%hfb_cq(this%flowja) if (this%ingnc > 0) call this%gnc%gnc_cq(this%flowja) if (this%insto > 0) call this%sto%sto_cq(this%flowja, this%x, this%xold) if (this%incsub > 0) call this%csub%csub_cq(this%dis%nodes, this%x, & diff --git a/src/Model/GroundWaterFlow/gwf3hfb8.f90 b/src/Model/GroundWaterFlow/gwf3hfb8.f90 index 2687b00ad3f..24534d9f017 100644 --- a/src/Model/GroundWaterFlow/gwf3hfb8.f90 +++ b/src/Model/GroundWaterFlow/gwf3hfb8.f90 @@ -3,6 +3,7 @@ module GwfHfbModule use KindModule, only: DP, I4B use Xt3dModule, only: Xt3dType + use GwfVscModule, only: GwfVscType use NumericalPackageModule, only: NumericalPackageType use BlockParserModule, only: BlockParserType use BaseDisModule, only: DisBaseType @@ -14,27 +15,34 @@ module GwfHfbModule public :: hfb_cr type, extends(NumericalPackageType) :: GwfHfbType - integer(I4B), pointer :: maxhfb => null() !max number of hfb's - integer(I4B), pointer :: nhfb => null() !number of hfb's - integer(I4B), dimension(:), pointer, contiguous :: noden => null() !first cell - integer(I4B), dimension(:), pointer, contiguous :: nodem => null() !second cell - integer(I4B), dimension(:), pointer, contiguous :: idxloc => null() !position in model ja - real(DP), dimension(:), pointer, contiguous :: hydchr => null() !hydraulic characteristic of the barrier - real(DP), dimension(:), pointer, contiguous :: csatsav => null() !value of condsat prior to hfb modification - real(DP), dimension(:), pointer, contiguous :: condsav => null() !saved conductance of combined npf and hfb - type(Xt3dType), pointer :: xt3d => null() !pointer to xt3d object - ! - integer(I4B), dimension(:), pointer, contiguous :: ibound => null() !pointer to model ibound - integer(I4B), dimension(:), pointer, contiguous :: icelltype => null() !pointer to model icelltype - integer(I4B), dimension(:), pointer, contiguous :: ihc => null() !pointer to model ihc - integer(I4B), dimension(:), pointer, contiguous :: ia => null() !pointer to model ia - integer(I4B), dimension(:), pointer, contiguous :: ja => null() !pointer to model ja - integer(I4B), dimension(:), pointer, contiguous :: jas => null() !pointer to model jas - integer(I4B), dimension(:), pointer, contiguous :: isym => null() !pointer to model isym - real(DP), dimension(:), pointer, contiguous :: condsat => null() !pointer to model condsat - real(DP), dimension(:), pointer, contiguous :: top => null() !pointer to model top - real(DP), dimension(:), pointer, contiguous :: bot => null() !pointer to model bot - real(DP), dimension(:), pointer, contiguous :: hwva => null() !pointer to model hwva + + type(GwfVscType), pointer :: vsc => null() !< viscosity object + integer(I4B), pointer :: maxhfb => null() !< max number of hfb's + integer(I4B), pointer :: nhfb => null() !< number of hfb's + integer(I4B), dimension(:), pointer, contiguous :: noden => null() !< first cell + integer(I4B), dimension(:), pointer, contiguous :: nodem => null() !< second cell + integer(I4B), dimension(:), pointer, contiguous :: idxloc => null() !< position in model ja + real(DP), dimension(:), pointer, contiguous :: hydchr => null() !< hydraulic characteristic of the barrier + real(DP), dimension(:), pointer, contiguous :: csatsav => null() !< value of condsat prior to hfb modification + real(DP), dimension(:), pointer, contiguous :: condsav => null() !< saved conductance of combined npf and hfb + type(Xt3dType), pointer :: xt3d => null() !< pointer to xt3d object + ! + integer(I4B), dimension(:), pointer, contiguous :: ibound => null() !< pointer to model ibound + integer(I4B), dimension(:), pointer, contiguous :: icelltype => null() !< pointer to model icelltype + integer(I4B), dimension(:), pointer, contiguous :: ihc => null() !< pointer to model ihc + integer(I4B), dimension(:), pointer, contiguous :: ia => null() !< pointer to model ia + integer(I4B), dimension(:), pointer, contiguous :: ja => null() !< pointer to model ja + integer(I4B), dimension(:), pointer, contiguous :: jas => null() !< pointer to model jas + integer(I4B), dimension(:), pointer, contiguous :: isym => null() !< pointer to model isym + real(DP), dimension(:), pointer, contiguous :: condsat => null() !< pointer to model condsat + real(DP), dimension(:), pointer, contiguous :: top => null() !< pointer to model top + real(DP), dimension(:), pointer, contiguous :: bot => null() !< pointer to model bot + real(DP), dimension(:), pointer, contiguous :: hwva => null() !< pointer to model hwva + real(DP), dimension(:), pointer, contiguous :: hnew => null() !< pointer to model xnew + ! + ! -- viscosity flag + integer(I4B), pointer :: ivsc => null() !< flag indicating if viscosity is active in the model + contains procedure :: hfb_ar procedure :: hfb_rp @@ -49,6 +57,8 @@ module GwfHfbModule procedure, private :: check_data procedure, private :: condsat_reset procedure, private :: condsat_modify + procedure, private :: get_visc_ratio + end type GwfHfbType contains @@ -87,7 +97,7 @@ subroutine hfb_cr(hfbobj, name_model, inunit, iout) return end subroutine hfb_cr - subroutine hfb_ar(this, ibound, xt3d, dis) + subroutine hfb_ar(this, ibound, xt3d, dis, invsc, vsc, hnew) ! ****************************************************************************** ! hfb_ar -- Allocate and read ! ****************************************************************************** @@ -101,7 +111,11 @@ subroutine hfb_ar(this, ibound, xt3d, dis) class(GwfHfbType) :: this integer(I4B), dimension(:), pointer, contiguous :: ibound type(Xt3dType), pointer :: xt3d - class(DisBaseType), pointer, intent(inout) :: dis + class(DisBaseType), pointer, intent(inout) :: dis !< discretization package + integer(I4B), pointer :: invsc !< indicates if viscosity package is active + type(GwfVscType), pointer, intent(in) :: vsc !< viscosity package + real(DP), dimension(:), pointer, contiguous, intent(inout) :: hnew !< pointer to model head array + ! -- local ! -- formats character(len=*), parameter :: fmtheader = & "(1x, /1x, 'HFB -- HORIZONTAL FLOW BARRIER PACKAGE, VERSION 8, ', & @@ -115,6 +129,7 @@ subroutine hfb_ar(this, ibound, xt3d, dis) this%dis => dis this%ibound => ibound this%xt3d => xt3d + this%hnew => hnew call mem_setptr(this%icelltype, 'ICELLTYPE', & create_mem_path(this%name_model, 'NPF')) @@ -133,6 +148,16 @@ subroutine hfb_ar(this, ibound, xt3d, dis) call this%read_dimensions() call this%allocate_arrays() ! + ! -- If vsc package active, set ivsc + if (invsc /= 0) then + this%ivsc = 1 + this%vsc => vsc + ! + ! -- Notify user via listing file viscosity accounted for by HFB package + write (this%iout, '(/1x,a,a)') 'VISCOSITY ACTIVE IN ', & + trim(this%filtyp)//' PACKAGE CALCULATIONS: '//trim(adjustl(this%packName)) + end if + ! ! -- return return end subroutine hfb_ar @@ -201,7 +226,7 @@ subroutine hfb_rp(this) return end subroutine hfb_rp - subroutine hfb_fc(this, kiter, njasln, amat, idxglo, rhs, hnew) + subroutine hfb_fc(this, kiter, njasln, amat, idxglo, rhs) ! ****************************************************************************** ! hfb_fc -- Fill amatsln for the following conditions: ! 1. Not Newton, and @@ -213,7 +238,7 @@ subroutine hfb_fc(this, kiter, njasln, amat, idxglo, rhs, hnew) ! SPECIFICATIONS: ! ------------------------------------------------------------------------------ ! -- modules - use ConstantsModule, only: DHALF, DZERO + use ConstantsModule, only: DHALF, DZERO, DONE ! -- dummy class(GwfHfbType) :: this integer(I4B) :: kiter @@ -221,18 +246,21 @@ subroutine hfb_fc(this, kiter, njasln, amat, idxglo, rhs, hnew) real(DP), dimension(njasln), intent(inout) :: amat integer(I4B), intent(in), dimension(:) :: idxglo real(DP), intent(inout), dimension(:) :: rhs - real(DP), intent(inout), dimension(:) :: hnew ! -- local integer(I4B) :: nodes, nja - integer(I4B) :: ihfb, n, m + integer(I4B) :: ihfb + integer(I4B) :: n, m integer(I4B) :: ipos integer(I4B) :: idiag, isymcon integer(I4B) :: ixt3d real(DP) :: cond, condhfb, aterm real(DP) :: fawidth, faheight real(DP) :: topn, topm, botn, botm + real(DP) :: viscratio ! ------------------------------------------------------------------------------ ! + ! -- initialize variables + viscratio = DONE nodes = this%dis%nodes nja = this%dis%con%nja if (associated(this%xt3d%ixt3d)) then @@ -248,7 +276,10 @@ subroutine hfb_fc(this, kiter, njasln, amat, idxglo, rhs, hnew) m = max(this%noden(ihfb), this%nodem(ihfb)) ! -- Skip if either cell is inactive. if (this%ibound(n) == 0 .or. this%ibound(m) == 0) cycle - !!! if(this%icelltype(n) == 1 .or. this%icelltype(m) == 1) then + !!! if(this%icelltype(n) == 1 .or. this%icelltype(m) == 1) then + if (this%ivsc /= 0) then + call this%get_visc_ratio(n, m, viscratio) + end if ! -- Compute scale factor for hfb correction if (this%hydchr(ihfb) > DZERO) then if (this%inewton == 0) then @@ -258,10 +289,10 @@ subroutine hfb_fc(this, kiter, njasln, amat, idxglo, rhs, hnew) botn = this%bot(n) botm = this%bot(m) if (this%icelltype(n) == 1) then - if (hnew(n) < topn) topn = hnew(n) + if (this%hnew(n) < topn) topn = this%hnew(n) end if if (this%icelltype(m) == 1) then - if (hnew(m) < topm) topm = hnew(m) + if (this%hnew(m) < topm) topm = this%hnew(m) end if if (this%ihc(this%jas(ipos)) == 2) then faheight = min(topn, topm) - max(botn, botm) @@ -269,16 +300,17 @@ subroutine hfb_fc(this, kiter, njasln, amat, idxglo, rhs, hnew) faheight = DHALF * ((topn - botn) + (topm - botm)) end if fawidth = this%hwva(this%jas(ipos)) - condhfb = this%hydchr(ihfb) * fawidth * faheight + condhfb = (this%hydchr(ihfb) * viscratio) * & + fawidth * faheight else - condhfb = this%hydchr(ihfb) + condhfb = this%hydchr(ihfb) * viscratio end if else condhfb = this%hydchr(ihfb) end if ! -- Make hfb corrections for xt3d call this%xt3d%xt3d_fhfb(kiter, nodes, nja, njasln, amat, idxglo, & - rhs, hnew, n, m, condhfb) + rhs, this%hnew, n, m, condhfb) end do ! else @@ -291,7 +323,13 @@ subroutine hfb_fc(this, kiter, njasln, amat, idxglo, rhs, hnew) n = this%noden(ihfb) m = this%nodem(ihfb) if (this%ibound(n) == 0 .or. this%ibound(m) == 0) cycle - if (this%icelltype(n) == 1 .or. this%icelltype(m) == 1) then + ! + if (this%ivsc /= 0) then + call this%get_visc_ratio(n, m, viscratio) + end if + ! + if (this%icelltype(n) == 1 .or. this%icelltype(m) == 1 .or. & + this%ivsc /= 0) then ! ! -- Calculate hfb conductance topn = this%top(n) @@ -299,10 +337,10 @@ subroutine hfb_fc(this, kiter, njasln, amat, idxglo, rhs, hnew) botn = this%bot(n) botm = this%bot(m) if (this%icelltype(n) == 1) then - if (hnew(n) < topn) topn = hnew(n) + if (this%hnew(n) < topn) topn = this%hnew(n) end if if (this%icelltype(m) == 1) then - if (hnew(m) < topm) topm = hnew(m) + if (this%hnew(m) < topm) topm = this%hnew(m) end if if (this%ihc(this%jas(ipos)) == 2) then faheight = min(topn, topm) - max(botn, botm) @@ -311,7 +349,8 @@ subroutine hfb_fc(this, kiter, njasln, amat, idxglo, rhs, hnew) end if if (this%hydchr(ihfb) > DZERO) then fawidth = this%hwva(this%jas(ipos)) - condhfb = this%hydchr(ihfb) * fawidth * faheight + condhfb = (this%hydchr(ihfb) * viscratio) * & + fawidth * faheight cond = aterm * condhfb / (aterm + condhfb) else cond = -aterm * this%hydchr(ihfb) @@ -341,7 +380,7 @@ subroutine hfb_fc(this, kiter, njasln, amat, idxglo, rhs, hnew) return end subroutine hfb_fc - subroutine hfb_cq(this, hnew, flowja) + subroutine hfb_cq(this, flowja) ! ****************************************************************************** ! hfb_cq -- flowja will automatically include the effects of the hfb ! for confined and newton cases when xt3d is not used. This method @@ -351,10 +390,9 @@ subroutine hfb_cq(this, hnew, flowja) ! SPECIFICATIONS: ! ------------------------------------------------------------------------------ ! -- modules - use ConstantsModule, only: DHALF, DZERO + use ConstantsModule, only: DHALF, DZERO, DONE ! -- dummy class(GwfHfbType) :: this - real(DP), intent(inout), dimension(:) :: hnew real(DP), intent(inout), dimension(:) :: flowja ! -- local integer(I4B) :: ihfb, n, m @@ -365,8 +403,12 @@ subroutine hfb_cq(this, hnew, flowja) real(DP) :: condhfb real(DP) :: fawidth, faheight real(DP) :: topn, topm, botn, botm + real(DP) :: viscratio ! ------------------------------------------------------------------------------ ! + ! -- initialize viscratio + viscratio = DONE + ! if (associated(this%xt3d%ixt3d)) then ixt3d = this%xt3d%ixt3d else @@ -380,7 +422,11 @@ subroutine hfb_cq(this, hnew, flowja) m = max(this%noden(ihfb), this%nodem(ihfb)) ! -- Skip if either cell is inactive. if (this%ibound(n) == 0 .or. this%ibound(m) == 0) cycle - !!! if(this%icelltype(n) == 1 .or. this%icelltype(m) == 1) then + !!! if(this%icelltype(n) == 1 .or. this%icelltype(m) == 1) then + if (this%ivsc /= 0) then + call this%get_visc_ratio(n, m, viscratio) + end if + ! ! -- Compute scale factor for hfb correction if (this%hydchr(ihfb) > DZERO) then if (this%inewton == 0) then @@ -390,10 +436,10 @@ subroutine hfb_cq(this, hnew, flowja) botn = this%bot(n) botm = this%bot(m) if (this%icelltype(n) == 1) then - if (hnew(n) < topn) topn = hnew(n) + if (this%hnew(n) < topn) topn = this%hnew(n) end if if (this%icelltype(m) == 1) then - if (hnew(m) < topm) topm = hnew(m) + if (this%hnew(m) < topm) topm = this%hnew(m) end if if (this%ihc(this%jas(ipos)) == 2) then faheight = min(topn, topm) - max(botn, botm) @@ -401,7 +447,8 @@ subroutine hfb_cq(this, hnew, flowja) faheight = DHALF * ((topn - botn) + (topm - botm)) end if fawidth = this%hwva(this%jas(ipos)) - condhfb = this%hydchr(ihfb) * fawidth * faheight + condhfb = (this%hydchr(ihfb) * viscratio) * & + fawidth * faheight else condhfb = this%hydchr(ihfb) end if @@ -409,7 +456,7 @@ subroutine hfb_cq(this, hnew, flowja) condhfb = this%hydchr(ihfb) end if ! -- Make hfb corrections for xt3d - call this%xt3d%xt3d_flowjahfb(n, m, hnew, flowja, condhfb) + call this%xt3d%xt3d_flowjahfb(n, m, this%hnew, flowja, condhfb) end do ! else @@ -420,10 +467,13 @@ subroutine hfb_cq(this, hnew, flowja) n = this%noden(ihfb) m = this%nodem(ihfb) if (this%ibound(n) == 0 .or. this%ibound(m) == 0) cycle - if (this%icelltype(n) == 1 .or. this%icelltype(m) == 1) then + if (this%icelltype(n) == 1 .or. this%icelltype(m) == 1 .or. & + this%ivsc /= 0) then ipos = this%dis%con%getjaindex(n, m) + ! + ! -- condsav already accnts for visc adjustment cond = this%condsav(ihfb) - qnm = cond * (hnew(m) - hnew(n)) + qnm = cond * (this%hnew(m) - this%hnew(n)) flowja(ipos) = qnm ipos = this%dis%con%getjaindex(m, n) flowja(ipos) = -qnm @@ -438,6 +488,40 @@ subroutine hfb_cq(this, hnew, flowja) return end subroutine hfb_cq + !> @brief Calculate the viscosity ratio + !! + !! Calculate the viscosity ratio applied to the hydraulic characteristic + !! provided by the user. The viscosity ratio is applicable only + !! when the hydraulic characteristic is specified as positive and will not + !! be applied when the hydchr is negative + !< + subroutine get_visc_ratio(this, n, m, hfbviscratio) + ! -- modules + use ConstantsModule, only: DONE + ! -- dummy + class(GwfHfbType) :: this + integer(I4B), intent(in) :: n, m + real(DP), intent(inout) :: hfbviscratio + ! -- local + real(DP) :: gwhdm, gwhdn, viscratio + integer(I4B) :: cellid +! ------------------------------------------------------------------------------ +! + viscratio = DONE + gwhdm = this%hnew(m) + gwhdn = this%hnew(n) + if (gwhdm > gwhdn) then + cellid = m + else if (gwhdn >= gwhdm) then + cellid = n + end if + call this%vsc%set_hfb_visc(cellid, viscratio) + hfbviscratio = viscratio + ! + ! -- return + return + end subroutine get_visc_ratio + subroutine hfb_da(this) ! ****************************************************************************** ! hfb_da -- Deallocate @@ -465,6 +549,7 @@ subroutine hfb_da(this) call mem_deallocate(this%idxloc) call mem_deallocate(this%csatsav) call mem_deallocate(this%condsav) + call mem_deallocate(this%ivsc) end if ! ! -- deallocate parent @@ -484,6 +569,8 @@ subroutine hfb_da(this) this%top => null() this%bot => null() this%hwva => null() + this%hnew => null() + this%vsc => null() ! ! -- return return @@ -509,9 +596,13 @@ subroutine allocate_scalars(this) call mem_allocate(this%maxhfb, 'MAXHFB', this%memoryPath) call mem_allocate(this%nhfb, 'NHFB', this%memoryPath) ! + ! -- allocate flag for determining if vsc active + call mem_allocate(this%ivsc, 'IVSC', this%memoryPath) + ! ! -- initialize this%maxhfb = 0 this%nhfb = 0 + this%ivsc = 0 ! ! -- return return @@ -838,7 +929,7 @@ subroutine condsat_modify(this) ! SPECIFICATIONS: ! ------------------------------------------------------------------------------ ! -- modules - use ConstantsModule, only: DHALF, DZERO + use ConstantsModule, only: DHALF, DZERO, DONE ! -- dummy class(GwfHfbType) :: this ! -- local @@ -847,14 +938,23 @@ subroutine condsat_modify(this) real(DP) :: cond, condhfb real(DP) :: fawidth, faheight real(DP) :: topn, topm, botn, botm + real(DP) :: viscratio ! ------------------------------------------------------------------------------ ! do ihfb = 1, this%nhfb + ! -- initialize variable + viscratio = DONE + ! ipos = this%idxloc(ihfb) cond = this%condsat(this%jas(ipos)) this%csatsav(ihfb) = cond n = this%noden(ihfb) m = this%nodem(ihfb) + ! -- account for viscosity when active + if (this%ivsc /= 0) then + call this%get_visc_ratio(n, m, viscratio) + end if + ! if (this%inewton == 1 .or. & (this%icelltype(n) == 0 .and. this%icelltype(m) == 0)) then ! @@ -870,7 +970,8 @@ subroutine condsat_modify(this) end if if (this%hydchr(ihfb) > DZERO) then fawidth = this%hwva(this%jas(ipos)) - condhfb = this%hydchr(ihfb) * fawidth * faheight + condhfb = this%hydchr(ihfb) * viscratio * & + fawidth * faheight cond = cond * condhfb / (cond + condhfb) else cond = -cond * this%hydchr(ihfb) diff --git a/src/Model/GroundWaterFlow/gwf3npf8.f90 b/src/Model/GroundWaterFlow/gwf3npf8.f90 index df7b595e381..47cfd211ded 100644 --- a/src/Model/GroundWaterFlow/gwf3npf8.f90 +++ b/src/Model/GroundWaterFlow/gwf3npf8.f90 @@ -99,7 +99,6 @@ module GwfNpfModule integer(I4B), pointer :: intvk => null() ! TVK (time-varying K) unit number (0 if unused) integer(I4B), pointer :: invsc => null() ! VSC (viscosity) unit number (0 if unused); viscosity leads to time-varying K's type(TvkType), pointer :: tvk => null() ! TVK object - !type(GwfVscType), pointer :: vsc => null() ! VSC object integer(I4B), pointer :: kchangeper => null() ! last stress period in which any node K (or K22, or K33) values were changed (0 if unchanged from start of simulation) integer(I4B), pointer :: kchangestp => null() ! last time step in which any node K (or K22, or K33) values were changed (0 if unchanged from start of simulation) integer(I4B), dimension(:), pointer, contiguous :: nodekchange => null() ! grid array of flags indicating for each node whether its K (or K22, or K33) value changed (1) at (kchangeper, kchangestp) or not (0) @@ -1097,6 +1096,11 @@ subroutine npf_da(this) deallocate (this%tvk) end if ! + ! -- VSC + if (this%invsc /= 0) then + nullify (this%vsc) + end if + ! ! -- Strings ! ! -- Scalars diff --git a/src/Model/GroundWaterFlow/gwf3vsc8.f90 b/src/Model/GroundWaterFlow/gwf3vsc8.f90 index c564c9d6477..06fe294a8f2 100644 --- a/src/Model/GroundWaterFlow/gwf3vsc8.f90 +++ b/src/Model/GroundWaterFlow/gwf3vsc8.f90 @@ -86,6 +86,7 @@ module GwfVscModule procedure, private :: set_npf_pointers procedure, public :: update_k_with_vsc procedure, private :: vsc_set_changed_at + procedure, public :: set_hfb_visc procedure :: set_concentration_pointer end type GwfVscType @@ -788,7 +789,6 @@ function calc_vsc_ratio(viscref, bndvisc) result(viscratio) end function calc_vsc_ratio function calc_bnd_viscosity(n, locvisc, locconc, viscref, dviscdc, cviscref, & - ! ctemp, ivisc, auxvar) result(viscbnd) ctemp, ivisc, a2, a3, a4, auxvar) result(viscbnd) ! ****************************************************************************** ! get_bnd_viscosity -- Return the viscosity of the boundary package using one of @@ -840,6 +840,32 @@ function calc_bnd_viscosity(n, locvisc, locconc, viscref, dviscdc, cviscref, & return end function calc_bnd_viscosity + !> @brief Account for viscosity in horizontal flow barriers + !! + !! Will return the viscosity associated with the upgradient node (cell) + !! to the HFB package for adjusting the hydraulic characteristic (hydchr) + !! of the barrier + !< + subroutine set_hfb_visc(this, cellid, hfbviscratio) + ! -- dummy variables + class(GwfVscType) :: this + integer(I4B), intent(in) :: cellid + ! -- return + real(DP), intent(inout) :: hfbviscratio + ! -- local + real(DP) :: visc +! ------------------------------------------------------------------------------ + ! + ! -- Retrieve viscosity for the passed node number + visc = this%visc(cellid) + ! + ! -- Calculate the viscosity ratio for the + hfbviscratio = calc_vsc_ratio(this%viscref, visc) + ! + ! -- return + return + end subroutine set_hfb_visc + !> @brief hit the hydraulic conductivity values with the ratio mu_o/mu !! !! This routine called after updating the viscosity values using the latest From a266ab36e27f95e1d43b6989f1ce628329703434 Mon Sep 17 00:00:00 2001 From: Eric Morway Date: Thu, 3 Nov 2022 05:17:32 -0700 Subject: [PATCH 20/31] deallocate in the wrong spot --- src/Model/GroundWaterFlow/gwf3hfb8.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Model/GroundWaterFlow/gwf3hfb8.f90 b/src/Model/GroundWaterFlow/gwf3hfb8.f90 index 24534d9f017..dcd8e060685 100644 --- a/src/Model/GroundWaterFlow/gwf3hfb8.f90 +++ b/src/Model/GroundWaterFlow/gwf3hfb8.f90 @@ -540,6 +540,7 @@ subroutine hfb_da(this) ! -- Scalars call mem_deallocate(this%maxhfb) call mem_deallocate(this%nhfb) + call mem_deallocate(this%ivsc) ! ! -- Arrays if (this%inunit > 0) then @@ -549,7 +550,6 @@ subroutine hfb_da(this) call mem_deallocate(this%idxloc) call mem_deallocate(this%csatsav) call mem_deallocate(this%condsav) - call mem_deallocate(this%ivsc) end if ! ! -- deallocate parent From 736b861f5f0790bef4d3dc1bad54ca9bb3c698c3 Mon Sep 17 00:00:00 2001 From: Eric Morway Date: Mon, 7 Nov 2022 05:36:34 -0800 Subject: [PATCH 21/31] Address review comments --- src/Model/GroundWaterFlow/gwf3.f90 | 6 +- src/Model/GroundWaterFlow/gwf3drn8.f90 | 36 +-- src/Model/GroundWaterFlow/gwf3ghb8.f90 | 3 +- src/Model/GroundWaterFlow/gwf3hfb8.f90 | 85 ++---- src/Model/GroundWaterFlow/gwf3lak8.f90 | 4 +- src/Model/GroundWaterFlow/gwf3maw8.f90 | 6 +- src/Model/GroundWaterFlow/gwf3npf8.f90 | 35 +-- src/Model/GroundWaterFlow/gwf3sfr8.f90 | 7 +- src/Model/GroundWaterFlow/gwf3vsc8.f90 | 355 +++++++++++++------------ 9 files changed, 241 insertions(+), 296 deletions(-) diff --git a/src/Model/GroundWaterFlow/gwf3.f90 b/src/Model/GroundWaterFlow/gwf3.f90 index e9963e0336d..dd4964b62af 100644 --- a/src/Model/GroundWaterFlow/gwf3.f90 +++ b/src/Model/GroundWaterFlow/gwf3.f90 @@ -428,7 +428,7 @@ subroutine gwf_ar(this) if (this%invsc > 0) call this%vsc%vsc_ar(this%ibound) if (this%inbuy > 0) call this%buy%buy_ar(this%npf, this%ibound) if (this%inhfb > 0) call this%hfb%hfb_ar(this%ibound, this%xt3d, this%dis, & - this%invsc, this%vsc, this%x) + this%invsc, this%vsc) if (this%insto > 0) call this%sto%sto_ar(this%dis, this%ibound) if (this%incsub > 0) call this%csub%csub_ar(this%dis, this%ibound) if (this%inmvr > 0) call this%mvr%mvr_ar() @@ -605,7 +605,7 @@ subroutine gwf_fc(this, kiter, amatsln, njasln, inwtflag) if (this%inbuy > 0) call this%buy%buy_fc(kiter, njasln, amatsln, & this%idxglo, this%rhs, this%x) if (this%inhfb > 0) call this%hfb%hfb_fc(kiter, njasln, amatsln, & - this%idxglo, this%rhs) + this%idxglo, this%rhs, this%x) if (this%ingnc > 0) call this%gnc%gnc_fc(kiter, amatsln) ! -- storage if (this%insto > 0) then @@ -926,7 +926,7 @@ subroutine gwf_cq(this, icnvg, isuppress_output) end do if (this%innpf > 0) call this%npf%npf_cq(this%x, this%flowja) if (this%inbuy > 0) call this%buy%buy_cq(this%x, this%flowja) - if (this%inhfb > 0) call this%hfb%hfb_cq(this%flowja) + if (this%inhfb > 0) call this%hfb%hfb_cq(this%x, this%flowja) if (this%ingnc > 0) call this%gnc%gnc_cq(this%flowja) if (this%insto > 0) call this%sto%sto_cq(this%flowja, this%x, this%xold) if (this%incsub > 0) call this%csub%csub_cq(this%dis%nodes, this%x, & diff --git a/src/Model/GroundWaterFlow/gwf3drn8.f90 b/src/Model/GroundWaterFlow/gwf3drn8.f90 index de2363a7679..45a329f9faa 100644 --- a/src/Model/GroundWaterFlow/gwf3drn8.f90 +++ b/src/Model/GroundWaterFlow/gwf3drn8.f90 @@ -1,13 +1,12 @@ module DrnModule use KindModule, only: DP, I4B - use ConstantsModule, only: DZERO, DONE, DTWO, LENFTYPE, LENPACKAGENAME, & - LENAUXNAME, LINELENGTH + use ConstantsModule, only: DZERO, DONE, DTWO, & + LENFTYPE, LENPACKAGENAME, LENAUXNAME, LINELENGTH use MemoryHelperModule, only: create_mem_path use SmoothingModule, only: sQSaturation, sQSaturationDerivative, & sQuadraticSaturation use BndModule, only: BndType use ObsModule, only: DefaultObsIdProcessor - use TdisModule, only: delt, totimc use TimeSeriesLinkModule, only: TimeSeriesLinkType, & GetTimeSeriesLinkFromList ! @@ -28,7 +27,6 @@ module DrnModule contains procedure :: allocate_scalars => drn_allocate_scalars procedure :: bnd_options => drn_options - procedure :: bnd_ad => drn_ad procedure :: bnd_ck => drn_ck procedure :: bnd_cf => drn_cf procedure :: bnd_fc => drn_fc @@ -94,36 +92,6 @@ subroutine drn_create(packobj, id, ibcnum, inunit, iout, namemodel, pakname) return end subroutine drn_create - !> @ brief Advance the drain boundary package - !! - !! Advance data in the drain boundary package. Overides the bnd_ad() - !! routine in the bndType parent class. The method advances time - !! series and observation data as well as updates the user-specified - !! conductance based on changes in viscosity when water enters from - !! the boundary - !< - subroutine drn_ad(this) - ! -- dummy variables - class(DrnType) :: this !< DrnType object - ! -- local variables - real(DP) :: begintime, endtime - ! - ! -- Initialize time variables - begintime = totimc - endtime = begintime + delt - ! - ! -- Advance the time series managers - call this%TsManager%ad() - call this%TasManager%ad() - ! - ! -- For each observation, push simulated value and corresponding - ! simulation time from "current" to "preceding" and reset - ! "current" value. - call this%obs%obs_ad() - ! - return - end subroutine drn_ad - subroutine drn_da(this) ! ****************************************************************************** ! drn_da -- deallocate diff --git a/src/Model/GroundWaterFlow/gwf3ghb8.f90 b/src/Model/GroundWaterFlow/gwf3ghb8.f90 index ddd876fe213..9757b1a16c4 100644 --- a/src/Model/GroundWaterFlow/gwf3ghb8.f90 +++ b/src/Model/GroundWaterFlow/gwf3ghb8.f90 @@ -1,6 +1,6 @@ module ghbmodule use KindModule, only: DP, I4B - use ConstantsModule, only: DZERO, LENFTYPE, LENPACKAGENAME, LENMEMSEPARATOR + use ConstantsModule, only: DZERO, LENFTYPE, LENPACKAGENAME use MemoryHelperModule, only: create_mem_path use BndModule, only: BndType use ObsModule, only: DefaultObsIdProcessor @@ -17,7 +17,6 @@ module ghbmodule character(len=LENPACKAGENAME) :: text = ' GHB' ! type, extends(BndType) :: GhbType - contains procedure :: bnd_options => ghb_options procedure :: bnd_ck => ghb_ck diff --git a/src/Model/GroundWaterFlow/gwf3hfb8.f90 b/src/Model/GroundWaterFlow/gwf3hfb8.f90 index dcd8e060685..52c4841a9d6 100644 --- a/src/Model/GroundWaterFlow/gwf3hfb8.f90 +++ b/src/Model/GroundWaterFlow/gwf3hfb8.f90 @@ -57,7 +57,6 @@ module GwfHfbModule procedure, private :: check_data procedure, private :: condsat_reset procedure, private :: condsat_modify - procedure, private :: get_visc_ratio end type GwfHfbType @@ -97,7 +96,7 @@ subroutine hfb_cr(hfbobj, name_model, inunit, iout) return end subroutine hfb_cr - subroutine hfb_ar(this, ibound, xt3d, dis, invsc, vsc, hnew) + subroutine hfb_ar(this, ibound, xt3d, dis, invsc, vsc) ! ****************************************************************************** ! hfb_ar -- Allocate and read ! ****************************************************************************** @@ -114,7 +113,6 @@ subroutine hfb_ar(this, ibound, xt3d, dis, invsc, vsc, hnew) class(DisBaseType), pointer, intent(inout) :: dis !< discretization package integer(I4B), pointer :: invsc !< indicates if viscosity package is active type(GwfVscType), pointer, intent(in) :: vsc !< viscosity package - real(DP), dimension(:), pointer, contiguous, intent(inout) :: hnew !< pointer to model head array ! -- local ! -- formats character(len=*), parameter :: fmtheader = & @@ -129,7 +127,6 @@ subroutine hfb_ar(this, ibound, xt3d, dis, invsc, vsc, hnew) this%dis => dis this%ibound => ibound this%xt3d => xt3d - this%hnew => hnew call mem_setptr(this%icelltype, 'ICELLTYPE', & create_mem_path(this%name_model, 'NPF')) @@ -154,8 +151,8 @@ subroutine hfb_ar(this, ibound, xt3d, dis, invsc, vsc, hnew) this%vsc => vsc ! ! -- Notify user via listing file viscosity accounted for by HFB package - write (this%iout, '(/1x,a,a)') 'VISCOSITY ACTIVE IN ', & - trim(this%filtyp)//' PACKAGE CALCULATIONS: '//trim(adjustl(this%packName)) + write (this%iout, '(/1x,a,a)') 'Viscosity active in ', & + trim(this%filtyp)//' Package calculations: '//trim(adjustl(this%packName)) end if ! ! -- return @@ -226,7 +223,7 @@ subroutine hfb_rp(this) return end subroutine hfb_rp - subroutine hfb_fc(this, kiter, njasln, amat, idxglo, rhs) + subroutine hfb_fc(this, kiter, njasln, amat, idxglo, rhs, hnew) ! ****************************************************************************** ! hfb_fc -- Fill amatsln for the following conditions: ! 1. Not Newton, and @@ -246,10 +243,10 @@ subroutine hfb_fc(this, kiter, njasln, amat, idxglo, rhs) real(DP), dimension(njasln), intent(inout) :: amat integer(I4B), intent(in), dimension(:) :: idxglo real(DP), intent(inout), dimension(:) :: rhs + real(DP), intent(inout), dimension(:) :: hnew ! -- local integer(I4B) :: nodes, nja - integer(I4B) :: ihfb - integer(I4B) :: n, m + integer(I4B) :: ihfb, n, m integer(I4B) :: ipos integer(I4B) :: idiag, isymcon integer(I4B) :: ixt3d @@ -278,7 +275,7 @@ subroutine hfb_fc(this, kiter, njasln, amat, idxglo, rhs) if (this%ibound(n) == 0 .or. this%ibound(m) == 0) cycle !!! if(this%icelltype(n) == 1 .or. this%icelltype(m) == 1) then if (this%ivsc /= 0) then - call this%get_visc_ratio(n, m, viscratio) + call this%vsc%get_visc_ratio(n, m, hnew(n), hnew(m), viscratio) end if ! -- Compute scale factor for hfb correction if (this%hydchr(ihfb) > DZERO) then @@ -289,10 +286,10 @@ subroutine hfb_fc(this, kiter, njasln, amat, idxglo, rhs) botn = this%bot(n) botm = this%bot(m) if (this%icelltype(n) == 1) then - if (this%hnew(n) < topn) topn = this%hnew(n) + if (hnew(n) < topn) topn = hnew(n) end if if (this%icelltype(m) == 1) then - if (this%hnew(m) < topm) topm = this%hnew(m) + if (hnew(m) < topm) topm = hnew(m) end if if (this%ihc(this%jas(ipos)) == 2) then faheight = min(topn, topm) - max(botn, botm) @@ -300,7 +297,7 @@ subroutine hfb_fc(this, kiter, njasln, amat, idxglo, rhs) faheight = DHALF * ((topn - botn) + (topm - botm)) end if fawidth = this%hwva(this%jas(ipos)) - condhfb = (this%hydchr(ihfb) * viscratio) * & + condhfb = this%hydchr(ihfb) * viscratio * & fawidth * faheight else condhfb = this%hydchr(ihfb) * viscratio @@ -310,7 +307,7 @@ subroutine hfb_fc(this, kiter, njasln, amat, idxglo, rhs) end if ! -- Make hfb corrections for xt3d call this%xt3d%xt3d_fhfb(kiter, nodes, nja, njasln, amat, idxglo, & - rhs, this%hnew, n, m, condhfb) + rhs, hnew, n, m, condhfb) end do ! else @@ -325,7 +322,7 @@ subroutine hfb_fc(this, kiter, njasln, amat, idxglo, rhs) if (this%ibound(n) == 0 .or. this%ibound(m) == 0) cycle ! if (this%ivsc /= 0) then - call this%get_visc_ratio(n, m, viscratio) + call this%vsc%get_visc_ratio(n, m, hnew(n), hnew(m), viscratio) end if ! if (this%icelltype(n) == 1 .or. this%icelltype(m) == 1 .or. & @@ -337,10 +334,10 @@ subroutine hfb_fc(this, kiter, njasln, amat, idxglo, rhs) botn = this%bot(n) botm = this%bot(m) if (this%icelltype(n) == 1) then - if (this%hnew(n) < topn) topn = this%hnew(n) + if (hnew(n) < topn) topn = hnew(n) end if if (this%icelltype(m) == 1) then - if (this%hnew(m) < topm) topm = this%hnew(m) + if (hnew(m) < topm) topm = hnew(m) end if if (this%ihc(this%jas(ipos)) == 2) then faheight = min(topn, topm) - max(botn, botm) @@ -349,7 +346,7 @@ subroutine hfb_fc(this, kiter, njasln, amat, idxglo, rhs) end if if (this%hydchr(ihfb) > DZERO) then fawidth = this%hwva(this%jas(ipos)) - condhfb = (this%hydchr(ihfb) * viscratio) * & + condhfb = this%hydchr(ihfb) * viscratio * & fawidth * faheight cond = aterm * condhfb / (aterm + condhfb) else @@ -380,7 +377,7 @@ subroutine hfb_fc(this, kiter, njasln, amat, idxglo, rhs) return end subroutine hfb_fc - subroutine hfb_cq(this, flowja) + subroutine hfb_cq(this, hnew, flowja) ! ****************************************************************************** ! hfb_cq -- flowja will automatically include the effects of the hfb ! for confined and newton cases when xt3d is not used. This method @@ -393,6 +390,7 @@ subroutine hfb_cq(this, flowja) use ConstantsModule, only: DHALF, DZERO, DONE ! -- dummy class(GwfHfbType) :: this + real(DP), intent(inout), dimension(:) :: hnew real(DP), intent(inout), dimension(:) :: flowja ! -- local integer(I4B) :: ihfb, n, m @@ -424,7 +422,7 @@ subroutine hfb_cq(this, flowja) if (this%ibound(n) == 0 .or. this%ibound(m) == 0) cycle !!! if(this%icelltype(n) == 1 .or. this%icelltype(m) == 1) then if (this%ivsc /= 0) then - call this%get_visc_ratio(n, m, viscratio) + call this%vsc%get_visc_ratio(n, m, hnew(n), hnew(m), viscratio) end if ! ! -- Compute scale factor for hfb correction @@ -436,10 +434,10 @@ subroutine hfb_cq(this, flowja) botn = this%bot(n) botm = this%bot(m) if (this%icelltype(n) == 1) then - if (this%hnew(n) < topn) topn = this%hnew(n) + if (hnew(n) < topn) topn = hnew(n) end if if (this%icelltype(m) == 1) then - if (this%hnew(m) < topm) topm = this%hnew(m) + if (hnew(m) < topm) topm = hnew(m) end if if (this%ihc(this%jas(ipos)) == 2) then faheight = min(topn, topm) - max(botn, botm) @@ -447,7 +445,7 @@ subroutine hfb_cq(this, flowja) faheight = DHALF * ((topn - botn) + (topm - botm)) end if fawidth = this%hwva(this%jas(ipos)) - condhfb = (this%hydchr(ihfb) * viscratio) * & + condhfb = this%hydchr(ihfb) * viscratio * & fawidth * faheight else condhfb = this%hydchr(ihfb) @@ -456,7 +454,7 @@ subroutine hfb_cq(this, flowja) condhfb = this%hydchr(ihfb) end if ! -- Make hfb corrections for xt3d - call this%xt3d%xt3d_flowjahfb(n, m, this%hnew, flowja, condhfb) + call this%xt3d%xt3d_flowjahfb(n, m, hnew, flowja, condhfb) end do ! else @@ -473,7 +471,7 @@ subroutine hfb_cq(this, flowja) ! ! -- condsav already accnts for visc adjustment cond = this%condsav(ihfb) - qnm = cond * (this%hnew(m) - this%hnew(n)) + qnm = cond * (hnew(m) - hnew(n)) flowja(ipos) = qnm ipos = this%dis%con%getjaindex(m, n) flowja(ipos) = -qnm @@ -488,40 +486,6 @@ subroutine hfb_cq(this, flowja) return end subroutine hfb_cq - !> @brief Calculate the viscosity ratio - !! - !! Calculate the viscosity ratio applied to the hydraulic characteristic - !! provided by the user. The viscosity ratio is applicable only - !! when the hydraulic characteristic is specified as positive and will not - !! be applied when the hydchr is negative - !< - subroutine get_visc_ratio(this, n, m, hfbviscratio) - ! -- modules - use ConstantsModule, only: DONE - ! -- dummy - class(GwfHfbType) :: this - integer(I4B), intent(in) :: n, m - real(DP), intent(inout) :: hfbviscratio - ! -- local - real(DP) :: gwhdm, gwhdn, viscratio - integer(I4B) :: cellid -! ------------------------------------------------------------------------------ -! - viscratio = DONE - gwhdm = this%hnew(m) - gwhdn = this%hnew(n) - if (gwhdm > gwhdn) then - cellid = m - else if (gwhdn >= gwhdm) then - cellid = n - end if - call this%vsc%set_hfb_visc(cellid, viscratio) - hfbviscratio = viscratio - ! - ! -- return - return - end subroutine get_visc_ratio - subroutine hfb_da(this) ! ****************************************************************************** ! hfb_da -- Deallocate @@ -569,7 +533,6 @@ subroutine hfb_da(this) this%top => null() this%bot => null() this%hwva => null() - this%hnew => null() this%vsc => null() ! ! -- return @@ -952,7 +915,7 @@ subroutine condsat_modify(this) m = this%nodem(ihfb) ! -- account for viscosity when active if (this%ivsc /= 0) then - call this%get_visc_ratio(n, m, viscratio) +! call this%get_visc_ratio(n, m, hnew(n), hnew(m), viscratio) end if ! if (this%inewton == 1 .or. & diff --git a/src/Model/GroundWaterFlow/gwf3lak8.f90 b/src/Model/GroundWaterFlow/gwf3lak8.f90 index 7df8ebc4924..67105a14f24 100644 --- a/src/Model/GroundWaterFlow/gwf3lak8.f90 +++ b/src/Model/GroundWaterFlow/gwf3lak8.f90 @@ -2388,7 +2388,7 @@ subroutine lak_calculate_conn_conductance(this, ilak, iconn, stage, head, cond) if (stage > head) then vscratio = this%viscratios(1, iconn) ! flow from aquifer to lake - else if (head > stage) then + else vscratio = this%viscratios(2, iconn) end if end if @@ -6404,7 +6404,7 @@ subroutine lak_activate_viscosity(this) this%memoryPath) do i = 1, this%maxbound do j = 1, 2 - this%viscratios(j, i) = DZERO + this%viscratios(j, i) = DONE end do end do write (this%iout, '(/1x,a)') 'VISCOSITY HAS BEEN ACTIVATED FOR LAK & diff --git a/src/Model/GroundWaterFlow/gwf3maw8.f90 b/src/Model/GroundWaterFlow/gwf3maw8.f90 index 45d54c678bd..295b84d8472 100644 --- a/src/Model/GroundWaterFlow/gwf3maw8.f90 +++ b/src/Model/GroundWaterFlow/gwf3maw8.f90 @@ -3894,7 +3894,7 @@ subroutine maw_calculate_conn_terms(this, n, j, icflow, cmaw, cterm, term, & ! flow out of well (flow is negative) if (flow < 0) then vscratio = this%viscratios(1, igwfnode) - else if (flow > 0) then + else vscratio = this%viscratios(2, igwfnode) end if end if @@ -4201,7 +4201,7 @@ subroutine maw_calculate_qpot(this, n, qnet) ! flow out of well (flow is negative) if (qnet < 0) then vscratio = this%viscratios(1, igwfnode) - else if (qnet > 0) then + else vscratio = this%viscratios(2, igwfnode) end if end if @@ -4897,7 +4897,7 @@ subroutine maw_activate_viscosity(this) this%memoryPath) do i = 1, this%maxbound do j = 1, 2 - this%viscratios(j, i) = DZERO + this%viscratios(j, i) = DONE end do end do write (this%iout, '(/1x,a)') 'VISCOSITY HAS BEEN ACTIVATED FOR MAW & diff --git a/src/Model/GroundWaterFlow/gwf3npf8.f90 b/src/Model/GroundWaterFlow/gwf3npf8.f90 index 47cfd211ded..ad4f027739c 100644 --- a/src/Model/GroundWaterFlow/gwf3npf8.f90 +++ b/src/Model/GroundWaterFlow/gwf3npf8.f90 @@ -1171,13 +1171,13 @@ subroutine npf_da(this) return end subroutine npf_da + !> @ brief Allocate scalars + !! + !! Allocate and initialize scalars for the VSC package. The base model + !! allocate scalars method is also called. + !! + !< subroutine allocate_scalars(this) -! ****************************************************************************** -! allocate_scalars -- Allocate scalar pointer variables -! ****************************************************************************** -! -! SPECIFICATIONS: -! ------------------------------------------------------------------------------ ! -- modules use MemoryHelperModule, only: create_mem_path ! -- dummy @@ -1274,10 +1274,19 @@ subroutine allocate_scalars(this) return end subroutine allocate_scalars + !> @ brief Stores a backup copy of hydraulic conductivity when the VSC + !! package is activate + !! + !! The K arrays (K11, etc.) get multiplied by the viscosity ratio + !! so that subsequent uses of K already take into account the effect + !! of viscosity. Thus the original user-specified K array values are + !! lost unless they are backed up in k11input, for example. In a new + !! stress period/time step, the values in k11input are multiplied by + !! the viscosity ratio, not k11 since it contains viscosity-adjusted + !! hydraulic conductivity values. + !! + !< subroutine store_original_k_arrays(this, ncells, njas) -! -! SPECIFICATIONS: -! ------------------------------------------------------------------------------ ! -- modules use MemoryManagerModule, only: mem_allocate ! -- dummy @@ -1291,12 +1300,8 @@ subroutine store_original_k_arrays(this, ncells, njas) ! -- Retain copy of user-specified K arrays do n = 1, ncells this%k11input(n) = this%k11(n) - if (this%ik22 /= 0) then - this%k22input(n) = this%k22(n) - end if - if (this%ik33 /= 0) then - this%k33input(n) = this%k33(n) - end if + this%k22input(n) = this%k22(n) + this%k33input(n) = this%k33(n) end do ! ! -- Return diff --git a/src/Model/GroundWaterFlow/gwf3sfr8.f90 b/src/Model/GroundWaterFlow/gwf3sfr8.f90 index a8dd1ddd950..0a081a33336 100644 --- a/src/Model/GroundWaterFlow/gwf3sfr8.f90 +++ b/src/Model/GroundWaterFlow/gwf3sfr8.f90 @@ -4043,7 +4043,7 @@ subroutine sfr_calc_cond(this, n, depth, cond, hsfr, htmp) if (hsfr > htmp) then ! strm stg > gw head vscratio = this%viscratios(1, n) - else if (htmp > hsfr) then + else vscratio = this%viscratios(2, n) end if end if @@ -5593,7 +5593,8 @@ end subroutine sfr_activate_density !> @brief Activate viscosity terms !! - !! Method to activate addition of viscosity terms for a SFR package reach. + !! Method to activate addition of viscosity terms for exhange + !! with groundwater along a SFR package reach. !! !< subroutine sfr_activate_viscosity(this) @@ -5611,7 +5612,7 @@ subroutine sfr_activate_viscosity(this) this%memoryPath) do i = 1, this%maxbound do j = 1, 2 - this%viscratios(j, i) = DZERO + this%viscratios(j, i) = DONE end do end do write (this%iout, '(/1x,a)') 'VISCOSITY HAS BEEN ACTIVATED FOR SFR & diff --git a/src/Model/GroundWaterFlow/gwf3vsc8.f90 b/src/Model/GroundWaterFlow/gwf3vsc8.f90 index 06fe294a8f2..5ce5a34cf8f 100644 --- a/src/Model/GroundWaterFlow/gwf3vsc8.f90 +++ b/src/Model/GroundWaterFlow/gwf3vsc8.f90 @@ -52,16 +52,12 @@ module GwfVscModule type(ConcentrationPointer), allocatable, dimension(:) :: modelconc !< concentration (or temperature) pointer for each solute (or heat) transport model - integer(I4B), pointer :: ik22overk => null() !< NPF flag that k22 is specified as anisotropy ratio - integer(I4B), pointer :: ik33overk => null() !< NPF flag that k33 is specified as anisotropy ratio real(DP), dimension(:), pointer, contiguous :: k11 => null() !< NPF hydraulic conductivity; if anisotropic, then this is Kx prior to rotation real(DP), dimension(:), pointer, contiguous :: k22 => null() !< NPF hydraulic conductivity; if specified then this is Ky prior to rotation real(DP), dimension(:), pointer, contiguous :: k33 => null() !< NPF hydraulic conductivity; if specified then this is Kz prior to rotation real(DP), dimension(:), pointer, contiguous :: k11input => null() !< NPF hydraulic conductivity as originally specified by the user real(DP), dimension(:), pointer, contiguous :: k22input => null() !< NPF hydraulic conductivity as originally specified by the user real(DP), dimension(:), pointer, contiguous :: k33input => null() !< NPF hydraulic conductivity as originally specified by the user - integer(I4B), pointer :: ik22 => null() !< NPF flag that k22 is specified - integer(I4B), pointer :: ik33 => null() !< NPF flag that k33 is specified integer(I4B), pointer :: kchangeper => null() ! last stress period in which any node K (or K22, or K33) values were changed (0 if unchanged from start of simulation) integer(I4B), pointer :: kchangestp => null() ! last time step in which any node K (or K22, or K33) values were changed (0 if unchanged from start of simulation) integer(I4B), dimension(:), pointer, contiguous :: nodekchange => null() ! grid array of flags indicating for each node whether its K (or K22, or K33) value changed (1) at (kchangeper, kchangestp) or not (0) @@ -86,7 +82,8 @@ module GwfVscModule procedure, private :: set_npf_pointers procedure, public :: update_k_with_vsc procedure, private :: vsc_set_changed_at - procedure, public :: set_hfb_visc + procedure, public :: calc_q_visc + procedure, public :: get_visc_ratio procedure :: set_concentration_pointer end type GwfVscType @@ -142,13 +139,12 @@ function calc_visc(ivisc, viscref, dviscdc, cviscref, conc, & return end function calc_visc + !> @ brief Create a new package object + !! + !! Create a new VSC Package object. + !! + !< subroutine vsc_cr(vscobj, name_model, inunit, iout) -! ****************************************************************************** -! vsc_cr -- Create a new VSC object -! ****************************************************************************** -! -! SPECIFICATIONS: -! ------------------------------------------------------------------------------ ! -- dummy type(GwfVscType), pointer :: vscobj character(len=*), intent(in) :: name_model @@ -176,15 +172,12 @@ subroutine vsc_cr(vscobj, name_model, inunit, iout) return end subroutine vsc_cr - !> @brief Read options and package data, or set from argument + !> @ brief Define viscosity package options and dimensions + !! + !! Define viscosity package options and dimensions + !! !< subroutine vsc_df(this, dis, vsc_input) -! ****************************************************************************** -! vsc_df -- Define -! ****************************************************************************** -! -! SPECIFICATIONS: -! ------------------------------------------------------------------------------ ! -- modules ! -- dummy class(GwfVscType) :: this !< this viscosity package @@ -193,8 +186,8 @@ subroutine vsc_df(this, dis, vsc_input) ! -- local ! -- formats character(len=*), parameter :: fmtvsc = & - "(1x,/1x,'VSC -- VISCOSITY PACKAGE, VERSION 1, 10/30/2022', & - &' INPUT READ FROM UNIT ', i0, //)" + "(1x,/1x,'VSC -- Viscosity Package, version 1, 11/15/2022', & + &' input read from unit ', i0, //)" ! ------------------------------------------------------------------------------ ! ! --print a message identifying the viscosity package @@ -232,13 +225,13 @@ subroutine vsc_df(this, dis, vsc_input) return end subroutine vsc_df + !> @ brief Allocate and read method for viscosity package + !! + !! Generic method to allocate and read static data for the viscosity + !! package available within the GWF model type. + !! + !< subroutine vsc_ar(this, ibound) -! ****************************************************************************** -! vsc_ar -- Allocate and Read -! ****************************************************************************** -! -! SPECIFICATIONS: -! ------------------------------------------------------------------------------ ! -- modules ! -- dummy class(GwfVscType) :: this @@ -265,9 +258,6 @@ end subroutine vsc_ar !! !< subroutine vsc_ar_bnd(this, packobj) - ! - ! SPECIFICATIONS: - ! ---------------------------------------------------------------------------- ! -- modules use BndModule, only: BndType use DrnModule, only: DrnType @@ -353,16 +343,12 @@ subroutine set_npf_pointers(this) ! -- Set pointers to other package variables ! -- NPF npfMemoryPath = create_mem_path(this%name_model, 'NPF') - call mem_setptr(this%ik22overk, 'IK22OVERK', npfMemoryPath) - call mem_setptr(this%ik33overk, 'IK33OVERK', npfMemoryPath) call mem_setptr(this%k11, 'K11', npfMemoryPath) call mem_setptr(this%k22, 'K22', npfMemoryPath) call mem_setptr(this%k33, 'K33', npfMemoryPath) call mem_setptr(this%k11input, 'K11INPUT', npfMemoryPath) call mem_setptr(this%k22input, 'K22INPUT', npfMemoryPath) call mem_setptr(this%k33input, 'K33INPUT', npfMemoryPath) - call mem_setptr(this%ik22, 'IK22', npfMemoryPath) - call mem_setptr(this%ik33, 'IK33', npfMemoryPath) call mem_setptr(this%kchangeper, 'KCHANGEPER', npfMemoryPath) call mem_setptr(this%kchangestp, 'KCHANGESTP', npfMemoryPath) call mem_setptr(this%nodekchange, 'NODEKCHANGE', npfMemoryPath) @@ -370,13 +356,12 @@ subroutine set_npf_pointers(this) return end subroutine set_npf_pointers + !> @ brief Read new period data in viscosity package + !! + !! Method to read and prepare period data for the VSC package. + !! + !< subroutine vsc_rp(this) -! ****************************************************************************** -! vsc_rp -- Check for new vsc period data -! ****************************************************************************** -! -! SPECIFICATIONS: -! ------------------------------------------------------------------------------ ! -- modules use TdisModule, only: kstp, kper ! -- dummy @@ -386,10 +371,10 @@ subroutine vsc_rp(this) integer(I4B) :: i ! -- formats character(len=*), parameter :: fmtc = & - "('VISCOSITY PACKAGE DOES NOT HAVE A CONCENTRATION SET & - &FOR SPECIES ',i0,'. ONE OR MORE MODEL NAMES MAY BE SPECIFIED & - &INCORRECTLY IN THE PACKAGEDATA BLOCK OR A GWF-GWT EXCHANGE MAY NEED & - &TO BE ACTIVATED.')" + "('Viscosity Package does not have a concentration set & + &for species ',i0,'. One or more model names may be specified & + &incorrectly in the PACKAGEDATA block or a GWF-GWT exchange may need & + &to be activated.')" ! ------------------------------------------------------------------------------ ! ! -- Check to make sure all concentration pointers have been set @@ -409,13 +394,14 @@ subroutine vsc_rp(this) return end subroutine vsc_rp + !> @ brief Advance the viscosity package + !! + !! Advance data in the VSC package. The method sets or + !! advances time series, time array series, and observation + !! data. + !! + !< subroutine vsc_ad(this) -! ****************************************************************************** -! vsc_ad -- Advance -! ****************************************************************************** -! -! SPECIFICATIONS: -! ------------------------------------------------------------------------------ ! -- dummy class(GwfVscType) :: this ! -- local @@ -428,7 +414,7 @@ subroutine vsc_ad(this) return end subroutine vsc_ad - !> @brief advance the boundary packages when viscosity is active + !> @brief Advance the boundary packages when viscosity is active !! !! Update the conductance values associate with inflow from a boundary !! when VSC package is active. @@ -548,7 +534,7 @@ subroutine vsc_ad_standard_bnd(packobj, hnew, visc, viscref, locelev, & ! -- local integer(I4B) :: n integer(I4B) :: node - real(DP) :: viscghb + real(DP) :: viscbnd ! ------------------------------------------------------------------------------- ! ! -- Process density terms for each GHB @@ -559,12 +545,12 @@ subroutine vsc_ad_standard_bnd(packobj, hnew, visc, viscref, locelev, & if (packobj%ibound(node) <= 0) cycle ! ! -- calculate the viscosity associcated with the boundary - viscghb = calc_bnd_viscosity(n, locvisc, locconc, viscref, dviscdc, & + viscbnd = calc_bnd_viscosity(n, locvisc, locconc, viscref, dviscdc, & cviscref, ctemp, ivisc, a2, a3, a4, & packobj%auxvar) ! ! -- update boundary conductance based on viscosity effects - packobj%bound(2, n) = update_bnd_cond(viscghb, viscref, & + packobj%bound(2, n) = update_bnd_cond(viscbnd, viscref, & packobj%condinput(n)) ! end do @@ -747,7 +733,7 @@ subroutine vsc_ad_maw(packobj, visc, viscref, elev, locvisc, locconc, & return end subroutine vsc_ad_maw - !> @brief apply bnd viscosity to the conductance term + !> @brief Apply viscosity to the conductance term !! !! When the viscosity package is active apply the viscosity ratio to the !! active boundary package's conductance term. @@ -765,7 +751,7 @@ function update_bnd_cond(bndvisc, viscref, spcfdcond) result(updatedcond) ! vscratio = calc_vsc_ratio(viscref, bndvisc) ! - ! -- calculate new conductance here!! + ! -- calculate new conductance here updatedcond = vscratio * spcfdcond ! ! -- Return @@ -788,18 +774,16 @@ function calc_vsc_ratio(viscref, bndvisc) result(viscratio) return end function calc_vsc_ratio + !> @ brief Calculate the boundary viscosity + !! + !! Return the viscosity of the boundary package using one of + !! the options in the following order of priority: + !! 1. Assign as aux variable in column with name 'VISCOSITY' + !! 2. Calculate using viscosity equation and nviscspecies aux columns + !! 3. If neither of those, then assign as viscref !! + !< function calc_bnd_viscosity(n, locvisc, locconc, viscref, dviscdc, cviscref, & ctemp, ivisc, a2, a3, a4, auxvar) result(viscbnd) -! ****************************************************************************** -! get_bnd_viscosity -- Return the viscosity of the boundary package using one of -! several different options in the following order of priority: -! 1. Assign as aux variable in column with name 'VISCOSITY' -! 2. Calculate using viscosity equation and nviscspecies aux columns -! 3. If neither of those, then assign as viscref -! ****************************************************************************** -! -! SPECIFICATIONS: -! ------------------------------------------------------------------------------ ! -- modules ! -- dummy integer(I4B), intent(in) :: n @@ -839,19 +823,50 @@ function calc_bnd_viscosity(n, locvisc, locconc, viscref, dviscdc, cviscref, & ! -- return return end function calc_bnd_viscosity + + !> @brief Calculate the viscosity ratio + !! + !! Calculate the viscosity ratio applied to the hydraulic characteristic + !! provided by the user. The viscosity ratio is applicable only + !! when the hydraulic characteristic is specified as positive and will not + !! be applied when the hydchr is negative + !< + subroutine get_visc_ratio(this, n, m, gwhdn, gwhdm, viscratio) + ! -- modules + use ConstantsModule, only: DONE + ! -- dummy + class(GwfVscType) :: this + integer(I4B), intent(in) :: n, m + real(DP), intent(in) :: gwhdn, gwhdm + real(DP), intent(inout) :: viscratio + ! -- loca + integer(I4B) :: cellid +! ------------------------------------------------------------------------------ +! + viscratio = DONE + if (gwhdm > gwhdn) then + cellid = m + else if (gwhdn >= gwhdm) then + cellid = n + end if + call this%calc_q_visc(cellid, viscratio) + ! + ! -- return + return + end subroutine get_visc_ratio - !> @brief Account for viscosity in horizontal flow barriers + !> @brief Account for viscosity in the aquiferhorizontal flow barriers !! !! Will return the viscosity associated with the upgradient node (cell) !! to the HFB package for adjusting the hydraulic characteristic (hydchr) !! of the barrier !< - subroutine set_hfb_visc(this, cellid, hfbviscratio) + subroutine calc_q_visc(this, cellid, viscratio) ! -- dummy variables class(GwfVscType) :: this integer(I4B), intent(in) :: cellid ! -- return - real(DP), intent(inout) :: hfbviscratio + real(DP), intent(inout) :: viscratio ! -- local real(DP) :: visc ! ------------------------------------------------------------------------------ @@ -860,13 +875,13 @@ subroutine set_hfb_visc(this, cellid, hfbviscratio) visc = this%visc(cellid) ! ! -- Calculate the viscosity ratio for the - hfbviscratio = calc_vsc_ratio(this%viscref, visc) + viscratio = calc_vsc_ratio(this%viscref, visc) ! ! -- return return - end subroutine set_hfb_visc + end subroutine calc_q_visc - !> @brief hit the hydraulic conductivity values with the ratio mu_o/mu + !> @brief Appled the viscosity ratio (mu_o/mu) to the hydraulic conductivity !! !! This routine called after updating the viscosity values using the latest !! concentration and/or temperature values. The ratio mu_o/mu, reference @@ -874,26 +889,22 @@ end subroutine set_hfb_visc !! for each cell. !< subroutine update_k_with_vsc(this) -! -! SPECIFICATIONS: -! ------------------------------------------------------------------------------ ! -- modules ! -- dummy class(GwfVscType) :: this + ! -- local integer(I4B) :: n + real(DP) :: viscratio ! ------------------------------------------------------------------------------ ! ! -- For viscosity-based K's, apply change of K to K11 by starting with ! user-specified K values and not the K's leftover from the last viscosity ! update. do n = 1, this%dis%nodes - this%k11(n) = this%k11input(n) * (this%viscref / this%visc(n)) - if (this%ik22 /= 0) then - this%k22(n) = this%k22input(n) * (this%viscref / this%visc(n)) - end if - if (this%ik33 /= 0) then - this%k33(n) = this%k33input(n) * (this%viscref / this%visc(n)) - end if + call this%calc_q_visc(n, viscratio) + this%k11(n) = this%k11input(n) * viscratio + this%k22(n) = this%k22input(n) * viscratio + this%k33(n) = this%k33input(n) * viscratio this%nodekchange(n) = 1 end do ! @@ -922,13 +933,12 @@ subroutine vsc_set_changed_at(this, kper, kstp) return end subroutine vsc_set_changed_at + !> @ brief Output viscosity package dependent-variable terms. + !! + !! Save calculated viscosity array to binary file + !! + !< subroutine vsc_ot_dv(this, idvfl) -! ****************************************************************************** -! vsc_ot_dv -- Save calculated viscosity array to binary file -! ****************************************************************************** -! -! SPECIFICATIONS: -! ------------------------------------------------------------------------------ ! -- dummy class(GwfVscType) :: this integer(I4B), intent(in) :: idvfl @@ -967,13 +977,12 @@ subroutine vsc_ot_dv(this, idvfl) return end subroutine vsc_ot_dv + !> @ brief Deallocate viscosity package memory + !! + !! Deallocate viscosity package scalars and arrays. + !! + !< subroutine vsc_da(this) -! ****************************************************************************** -! vsc_da -- Deallocate -! ****************************************************************************** -! -! SPECIFICATIONS: -! ------------------------------------------------------------------------------ ! -- modules ! -- dummy class(GwfVscType) :: this @@ -1004,16 +1013,12 @@ subroutine vsc_da(this) call mem_deallocate(this%a4) ! ! -- Nullify pointers to other package variables - nullify (this%ik22overk) - nullify (this%ik33overk) nullify (this%k11) nullify (this%k22) nullify (this%k33) nullify (this%k11input) nullify (this%k22input) nullify (this%k33input) - nullify (this%ik22) - nullify (this%ik33) nullify (this%kchangeper) nullify (this%kchangestp) nullify (this%nodekchange) @@ -1025,13 +1030,12 @@ subroutine vsc_da(this) return end subroutine vsc_da + !> @ brief Read dimensions + !! + !! Read dimensions for the viscosity package + !! + !< subroutine read_dimensions(this) -! ****************************************************************************** -! read_dimensions -- Read the dimensions for this package -! ****************************************************************************** -! -! SPECIFICATIONS: -! ------------------------------------------------------------------------------ ! -- modules ! -- dummy class(GwfVscType), intent(inout) :: this @@ -1048,7 +1052,7 @@ subroutine read_dimensions(this) ! ! -- parse dimensions block if detected if (isfound) then - write (this%iout, '(/1x,a)') 'PROCESSING VSC DIMENSIONS' + write (this%iout, '(/1x,a)') 'Processing VSC DIMENSIONS block' do call this%parser%GetNextLine(endOfBlock) if (endOfBlock) exit @@ -1059,20 +1063,20 @@ subroutine read_dimensions(this) write (this%iout, '(4x,a,i0)') 'NVISCSPECIES = ', this%nviscspecies case default write (errmsg, '(4x,a,a)') & - 'UNKNOWN VSC DIMENSION: ', trim(keyword) + 'unknown VSC dimension: ', trim(keyword) call store_error(errmsg) call this%parser%StoreErrorUnit() end select end do - write (this%iout, '(1x,a)') 'END OF VSC DIMENSIONS' + write (this%iout, '(1x,a)') 'End of VSC DIMENSIONS block' else - call store_error('REQUIRED VSC DIMENSIONS BLOCK NOT FOUND.') + call store_error('Required VSC DIMENSIONS block not found.') call this%parser%StoreErrorUnit() end if ! ! -- check dimension if (this%nviscspecies < 1) then - call store_error('NVISCSPECIES MUST BE GREATER THAN ZERO.') + call store_error('NVISCSPECIES must be greater than zero.') call this%parser%StoreErrorUnit() end if ! @@ -1082,7 +1086,7 @@ end subroutine read_dimensions !> @ brief Read data for package !! - !! Method to read data for the package. + !! Method to read data for the viscosity package. !! !< subroutine read_packagedata(this) @@ -1101,9 +1105,9 @@ subroutine read_packagedata(this) character(len=16) :: c16 ! -- format character(len=*), parameter :: fmterr = & - "('INVALID VALUE FOR IRHOSPEC (',i0,') DETECTED IN VSC PACKAGE. & - &IRHOSPEC MUST BE > 0 AND <= NVISCSPECIES, AND DUPLICATE VALUES & - &ARE NOT ALLOWED.')" + "('Invalid value for IRHOSPEC (',i0,') detected in VSC Package. & + &IRHOSPEC must be > 0 and <= NVISCSPECIES, and duplicate values & + &are not allowed.')" ! ------------------------------------------------------------------------------ ! ! -- initialize @@ -1118,7 +1122,7 @@ subroutine read_packagedata(this) ! ! -- parse packagedata block if (isfound) then - write (this%iout, '(1x,a)') 'PROCESSING VSC PACKAGEDATA' + write (this%iout, '(1x,a)') 'Procesing VSC PACKAGEDATA block' do call this%parser%GetNextLine(endOfBlock) if (endOfBlock) exit @@ -1140,9 +1144,9 @@ subroutine read_packagedata(this) ! if (this%cauxspeciesname(iviscspec) == 'TEMPERATURE') then if (this%idxtmpr > 0) then - write (errmsg, '(a)') 'MORE THAN ONE SPECIES IN VSC INPUT IDENTIFIED & - &AS "TEMPERATURE". ONLY ONE SPECIES MAY BE DESIGNATED AS & - &TEMPERATURE.' + write (errmsg, '(a)') 'More than one species in VSC input identified & + &as "TEMPERATURE". Only one species may be designated as & + &"TEMPERATURE".' call store_error(errmsg) else this%idxtmpr = iviscspec @@ -1152,6 +1156,9 @@ subroutine read_packagedata(this) end if end if end do + else + call store_error('Required VSC PACKAGEDATA block not found.') + call this%parser%StoreErrorUnit() end if ! ! -- Check for errors. @@ -1160,9 +1167,9 @@ subroutine read_packagedata(this) end if ! ! -- write packagedata information - write (this%iout, '(/,1x,a)') 'SUMMARY OF SPECIES INFORMATION IN VSC PACKAGE' + write (this%iout, '(/,1x,a)') 'Summary of species information in VSC Package' write (this%iout, '(1a11,5a17)') & - 'SPECIES', 'DVISCDC', 'CVISCREF', 'MODEL', 'AUXSPECIESNAME' + 'Species', 'DVISCDC', 'CVISCREF', 'Model', 'AUXSPECIESNAME' do iviscspec = 1, this%nviscspecies write (c10, '(i0)') iviscspec line = ' '//adjustr(c10) @@ -1181,7 +1188,7 @@ subroutine read_packagedata(this) ! -- deallocate deallocate (itemp) ! - write (this%iout, '(/,1x,a)') 'END OF VSC PACKAGEDATA' + write (this%iout, '(/,1x,a)') 'End of VSC PACKAGEDATA block' ! ! -- return return @@ -1204,13 +1211,13 @@ subroutine set_packagedata(this, input_data) end subroutine set_packagedata + !> @brief Calculate fluid viscosity + !! + !! Calculates fluid viscosity based on concentration or + !! temperature + !! + !< subroutine vsc_calcvisc(this) -! ****************************************************************************** -! vsc_calcvisc -- calculate fluid viscosity from concentration -! ****************************************************************************** -! -! SPECIFICATIONS: -! ------------------------------------------------------------------------------ ! -- dummy class(GwfVscType) :: this @@ -1233,13 +1240,18 @@ subroutine vsc_calcvisc(this) this%visc(n) = calc_visc(this%ivisc, this%viscref, this%dviscdc, & this%cviscref, this%ctemp, this%a2, & this%a3, this%a4) - end do ! ! -- Return return end subroutine vsc_calcvisc + !> @ brief Allocate scalars + !! + !! Allocate and initialize scalars for the VSC package. The base model + !! allocate scalars method is also called. + !! + !< subroutine allocate_scalars(this) ! ****************************************************************************** ! allocate_scalars @@ -1287,13 +1299,12 @@ subroutine allocate_scalars(this) return end subroutine allocate_scalars + !> @ brief Allocate arrays + !! + !! Allocate and initialize arrays for the VSC package. + !! + !< subroutine allocate_arrays(this, nodes) -! ****************************************************************************** -! allocate_arrays -! ****************************************************************************** -! -! SPECIFICATIONS: -! ------------------------------------------------------------------------------ ! -- modules ! -- dummy class(GwfVscType) :: this @@ -1333,13 +1344,12 @@ subroutine allocate_arrays(this, nodes) return end subroutine allocate_arrays + !> @ brief Read Options block + !! + !! Reads the options block inside the VSC package. + !! + !< subroutine read_options(this) -! ****************************************************************************** -! read_options -! ****************************************************************************** -! -! SPECIFICATIONS: -! ------------------------------------------------------------------------------ ! -- modules use OpenSpecModule, only: access, form use InputOutputModule, only: urword, getunit, urdaux, openfile @@ -1354,14 +1364,14 @@ subroutine read_options(this) logical :: isfound, endOfBlock ! -- formats character(len=*), parameter :: fmtfileout = & - "(1x, 'VSC', 1x, a, 1x, 'WILL BE SAVED TO FILE: ', & - &a, /4x, 'OPENED ON UNIT: ', I7)" + "(1x, 'VSC', 1x, a, 1x, 'Will be saved to file: ', & + &a, /4x, 'opened on unit: ', I7)" character(len=*), parameter :: fmtlinear = & - "(/,1x,'VISCOSITY WILL VARY LINEARLY WITH TEMPERATURE & - &CHANGE ')" + "(/,1x,'Viscosity will vary linearly with temperature & + &change ')" character(len=*), parameter :: fmtnonlinear = & - "(/,1x,'VISCOSITY WILL VARY NON-LINEARLY WITH TEMPERATURE & - &CHANGE ')" + "(/,1x,'Viscosity will vary non-linearly with temperature & + &change ')" ! ------------------------------------------------------------------------------ ! ! -- get options block @@ -1370,7 +1380,7 @@ subroutine read_options(this) ! ! -- parse options block if detected if (isfound) then - write (this%iout, '(1x,a)') 'PROCESSING VSC OPTIONS' + write (this%iout, '(1x,a)') 'Processing VSC OPTIONS block' do call this%parser%GetNextLine(endOfBlock) if (endOfBlock) exit @@ -1379,7 +1389,7 @@ subroutine read_options(this) case ('VISCREF') this%viscref = this%parser%GetDouble() write (this%iout, '(4x,a,1pg15.6)') & - 'REFERENCE VISCOSITY HAS BEEN SET TO: ', & + 'Reference viscosity has been set to: ', & this%viscref case ('VISCOSITY') call this%parser%GetStringCaps(keyword) @@ -1391,8 +1401,8 @@ subroutine read_options(this) write (this%iout, fmtfileout) & 'VISCOSITY', fname, this%ioutvisc else - errmsg = 'OPTIONAL VISCOSITY KEYWORD MUST BE '// & - 'FOLLOWED BY FILEOUT' + errmsg = 'Optional VISCOSITY keyword must be '// & + 'followed by FILEOUT' call store_error(errmsg) end if case ('THERMAL_VISCOSITY_FUNC') @@ -1409,8 +1419,8 @@ subroutine read_options(this) this%a4 = this%parser%GetDouble() ! ! -- Write viscosity function selection to lst file - write (this%iout, '(/,1x,a,a,a)') 'CONSTANTS USED IN ', & - trim(keyword2), ' VISCOSITY FORMULATION ARE ' + write (this%iout, '(/,1x,a,a,a)') 'Constants used in ', & + trim(keyword2), ' viscosity formulation are ' write (this%iout, '(1x,a)') & ' A2, A3, A4' line = ' ' @@ -1424,7 +1434,7 @@ subroutine read_options(this) end select case default - write (errmsg, '(4x,a,a)') '**ERROR. UNKNOWN VSC OPTION: ', & + write (errmsg, '(4x,a,a)') '**Error. Unknown VSC option: ', & trim(keyword) call store_error(errmsg) call this%parser%StoreErrorUnit() @@ -1433,35 +1443,35 @@ subroutine read_options(this) ! if (this%thermivisc == 1) then if (this%a2 == 0.0) then - write (errmsg, '(a)') 'LINEAR OPTION SELECTED FOR VARYING & - &VISCOSITY WITH TEMPERTURE, BUT A1, A SURROGATE FOR & - &dVISC/dT, SET EQUAL TO 0.0' + write (errmsg, '(a)') 'LINEAR option selected for varying & + &viscosity with temperature, but A1, a surrogate for & + &dVISC/dT, set equal to 0.0' call store_error(errmsg) end if end if if (this%thermivisc > 1) then if (this%a2 == 0) then - write (warnmsg, '(a)') 'NONLINEAR OPTION SELECTED FOR & - &VARYING VISCOSITY WITH TEMPERATURE, BUT A2 SET EQUAL TO & - &ZERO WHICH MAY LEAD TO UNINTENDED VALUES FOR VISCOSITY' + write (warnmsg, '(a)') 'NONLINEAR option selected for & + &varying viscosity with temperature, but A2 set equal to & + &zero which may lead to unintended values for viscosity' call store_warning(errmsg) end if if (this%a3 == 0) then - write (warnmsg, '(a)') 'NONLINEAR OPTION SELECTED FOR & - &VARYING VISCOSITY WITH TEMPERATURE, BUT A3 SET EQUAL TO & - &ZERO WHICH MAY LEAD TO UNINTENDED VALUES FOR VISCOSITY' + write (warnmsg, '(a)') 'NONLINEAR option selected for & + &varying viscosity with temperature,, but A3 set equal to & + &zero which may lead to unintended values for viscosity' call store_warning(warnmsg) end if if (this%a4 == 0) then - write (warnmsg, '(a)') 'NONLINEAR OPTION SELECTED FOR & - &VARYING VISCOSITY WITH TEMPERATURE, BUT A4 SET EQUAL TO & - &ZERO WHICH MAY LEAD TO UNINTENDED VALUES FOR VISCOSITY' + write (warnmsg, '(a)') 'NONLINEAR option selected for & + &varying viscosity with temperature, BUT A4 SET EQUAL TO & + &zero which may lead to unintended values for viscosity' call store_warning(warnmsg) end if end if end if ! - write (this%iout, '(/,1x,a)') 'END OF VSC OPTIONS' + write (this%iout, '(/,1x,a)') 'end of VSC options block' ! ! -- Return return @@ -1479,16 +1489,15 @@ subroutine set_options(this, input_data) return end subroutine set_options + !> @ brief Set pointers to concentration(s) + !! + !! Pass in a gwt model name, concentration array, and ibound, + !! and store a pointer to these in the VSC package so that + !! viscosity can be calculated from them. This routine is called + !! from the gwfgwt exchange in the exg_ar() method. + !! + !< subroutine set_concentration_pointer(this, modelname, conc, icbund, istmpr) -! ****************************************************************************** -! set_concentration_pointer -- pass in a gwt model name, concentration array -! and ibound, and store a pointer to these in the VSC package so that -! viscosity can be calculated from them. -! This routine is called from the gwfgwt exchange in the exg_ar() method. -! ****************************************************************************** -! -! SPECIFICATIONS: -! ------------------------------------------------------------------------------ ! -- modules ! -- dummy class(GwfVscType) :: this From 2bfec827d2141eeab8d640795f0d6a3913c05fe9 Mon Sep 17 00:00:00 2001 From: Eric Morway Date: Mon, 7 Nov 2022 05:51:34 -0800 Subject: [PATCH 22/31] fprettify --- src/Model/GroundWaterFlow/gwf3lak8.f90 | 2 +- src/Model/GroundWaterFlow/gwf3npf8.f90 | 8 ++++---- src/Model/GroundWaterFlow/gwf3sfr8.f90 | 2 +- src/Model/GroundWaterFlow/gwf3vsc8.f90 | 20 ++++++++++---------- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/Model/GroundWaterFlow/gwf3lak8.f90 b/src/Model/GroundWaterFlow/gwf3lak8.f90 index 67105a14f24..cfb921248fa 100644 --- a/src/Model/GroundWaterFlow/gwf3lak8.f90 +++ b/src/Model/GroundWaterFlow/gwf3lak8.f90 @@ -2388,7 +2388,7 @@ subroutine lak_calculate_conn_conductance(this, ilak, iconn, stage, head, cond) if (stage > head) then vscratio = this%viscratios(1, iconn) ! flow from aquifer to lake - else + else vscratio = this%viscratios(2, iconn) end if end if diff --git a/src/Model/GroundWaterFlow/gwf3npf8.f90 b/src/Model/GroundWaterFlow/gwf3npf8.f90 index ad4f027739c..0ad9fd70bc0 100644 --- a/src/Model/GroundWaterFlow/gwf3npf8.f90 +++ b/src/Model/GroundWaterFlow/gwf3npf8.f90 @@ -1274,14 +1274,14 @@ subroutine allocate_scalars(this) return end subroutine allocate_scalars - !> @ brief Stores a backup copy of hydraulic conductivity when the VSC + !> @ brief Stores a backup copy of hydraulic conductivity when the VSC !! package is activate !! - !! The K arrays (K11, etc.) get multiplied by the viscosity ratio + !! The K arrays (K11, etc.) get multiplied by the viscosity ratio !! so that subsequent uses of K already take into account the effect - !! of viscosity. Thus the original user-specified K array values are + !! of viscosity. Thus the original user-specified K array values are !! lost unless they are backed up in k11input, for example. In a new - !! stress period/time step, the values in k11input are multiplied by + !! stress period/time step, the values in k11input are multiplied by !! the viscosity ratio, not k11 since it contains viscosity-adjusted !! hydraulic conductivity values. !! diff --git a/src/Model/GroundWaterFlow/gwf3sfr8.f90 b/src/Model/GroundWaterFlow/gwf3sfr8.f90 index 0a081a33336..649445fa55e 100644 --- a/src/Model/GroundWaterFlow/gwf3sfr8.f90 +++ b/src/Model/GroundWaterFlow/gwf3sfr8.f90 @@ -5593,7 +5593,7 @@ end subroutine sfr_activate_density !> @brief Activate viscosity terms !! - !! Method to activate addition of viscosity terms for exhange + !! Method to activate addition of viscosity terms for exhange !! with groundwater along a SFR package reach. !! !< diff --git a/src/Model/GroundWaterFlow/gwf3vsc8.f90 b/src/Model/GroundWaterFlow/gwf3vsc8.f90 index 5ce5a34cf8f..da1aa515f53 100644 --- a/src/Model/GroundWaterFlow/gwf3vsc8.f90 +++ b/src/Model/GroundWaterFlow/gwf3vsc8.f90 @@ -228,7 +228,7 @@ end subroutine vsc_df !> @ brief Allocate and read method for viscosity package !! !! Generic method to allocate and read static data for the viscosity - !! package available within the GWF model type. + !! package available within the GWF model type. !! !< subroutine vsc_ar(this, ibound) @@ -396,8 +396,8 @@ end subroutine vsc_rp !> @ brief Advance the viscosity package !! - !! Advance data in the VSC package. The method sets or - !! advances time series, time array series, and observation + !! Advance data in the VSC package. The method sets or + !! advances time series, time array series, and observation !! data. !! !< @@ -733,7 +733,7 @@ subroutine vsc_ad_maw(packobj, visc, viscref, elev, locvisc, locconc, & return end subroutine vsc_ad_maw - !> @brief Apply viscosity to the conductance term + !> @brief Apply viscosity to the conductance term !! !! When the viscosity package is active apply the viscosity ratio to the !! active boundary package's conductance term. @@ -823,7 +823,7 @@ function calc_bnd_viscosity(n, locvisc, locconc, viscref, dviscdc, cviscref, & ! -- return return end function calc_bnd_viscosity - + !> @brief Calculate the viscosity ratio !! !! Calculate the viscosity ratio applied to the hydraulic characteristic @@ -853,7 +853,7 @@ subroutine get_visc_ratio(this, n, m, gwhdn, gwhdm, viscratio) ! ! -- return return - end subroutine get_visc_ratio + end subroutine get_visc_ratio !> @brief Account for viscosity in the aquiferhorizontal flow barriers !! @@ -1030,7 +1030,7 @@ subroutine vsc_da(this) return end subroutine vsc_da - !> @ brief Read dimensions + !> @ brief Read dimensions !! !! Read dimensions for the viscosity package !! @@ -1213,7 +1213,7 @@ end subroutine set_packagedata !> @brief Calculate fluid viscosity !! - !! Calculates fluid viscosity based on concentration or + !! Calculates fluid viscosity based on concentration or !! temperature !! !< @@ -1344,7 +1344,7 @@ subroutine allocate_arrays(this, nodes) return end subroutine allocate_arrays - !> @ brief Read Options block + !> @ brief Read Options block !! !! Reads the options block inside the VSC package. !! @@ -1491,7 +1491,7 @@ end subroutine set_options !> @ brief Set pointers to concentration(s) !! - !! Pass in a gwt model name, concentration array, and ibound, + !! Pass in a gwt model name, concentration array, and ibound, !! and store a pointer to these in the VSC package so that !! viscosity can be calculated from them. This routine is called !! from the gwfgwt exchange in the exg_ar() method. From a93e6334c76513d62d51e57147df7faedafd2619 Mon Sep 17 00:00:00 2001 From: Eric Morway Date: Tue, 8 Nov 2022 04:35:43 -0800 Subject: [PATCH 23/31] refactor vsc definition file and make corresponding changes in autotests and vsc src code --- autotest/test_gwf_vsc01.py | 5 +- autotest/test_gwf_vsc02.py | 5 +- autotest/test_gwf_vsc03_sfr.py | 6 +- autotest/test_gwf_vsc04_lak.py | 5 +- autotest/test_gwf_vsc05_hfb.py | 5 +- doc/mf6io/mf6ivar/dfn/gwf-vsc.dfn | 94 ++++++++++---------------- src/Model/GroundWaterFlow/gwf3vsc8.f90 | 63 ++++++++++------- 7 files changed, 97 insertions(+), 86 deletions(-) diff --git a/autotest/test_gwf_vsc01.py b/autotest/test_gwf_vsc01.py index e7d17f1f0fd..ac89615c81c 100644 --- a/autotest/test_gwf_vsc01.py +++ b/autotest/test_gwf_vsc01.py @@ -144,7 +144,10 @@ def build_model(idx, dir): gwf, viscref=8.904e-4, viscosity_filerecord=vsc_filerecord, - viscosityfuncrecord=[("nonlinear", 10.0, 248.37, 133.16)], + thermal_formulation="nonlinear", + thermal_a2=10.0, + thermal_a3=248.37, + thermal_a4=133.16, nviscspecies=len(vsc_pd), packagedata=vsc_pd, pname="vsc", diff --git a/autotest/test_gwf_vsc02.py b/autotest/test_gwf_vsc02.py index 316b1ad877b..63eaccea970 100644 --- a/autotest/test_gwf_vsc02.py +++ b/autotest/test_gwf_vsc02.py @@ -147,7 +147,10 @@ def build_model(idx, dir): gwf, viscref=8.904e-4, viscosity_filerecord=vsc_filerecord, - viscosityfuncrecord=[("nonlinear", 10.0, 248.37, 133.16)], + thermal_formulation="nonlinear", + thermal_a2=10.0, + thermal_a3=248.37, + thermal_a4=133.16, nviscspecies=len(vsc_pd), packagedata=vsc_pd, pname="vsc", diff --git a/autotest/test_gwf_vsc03_sfr.py b/autotest/test_gwf_vsc03_sfr.py index 2bd580a32e5..001b7dbebb3 100644 --- a/autotest/test_gwf_vsc03_sfr.py +++ b/autotest/test_gwf_vsc03_sfr.py @@ -100,6 +100,7 @@ def topElev_sfrCentered(x, y): # MODFLOW 6 flopy GWF & GWT simulation object (sim) is returned # + def build_model(idx, dir): # Base simulation and model name and workspace ws = dir @@ -189,7 +190,10 @@ def build_model(idx, dir): gwf, viscref=viscref, viscosity_filerecord=vsc_filerecord, - viscosityfuncrecord=[("nonlinear", 10.0, 248.37, 133.15)], + thermal_formulation="nonlinear", + thermal_a2=10.0, + thermal_a3=248.37, + thermal_a4=133.16, nviscspecies=len(vsc_pd), packagedata=vsc_pd, pname="vsc", diff --git a/autotest/test_gwf_vsc04_lak.py b/autotest/test_gwf_vsc04_lak.py index 5706b29960c..5296ff729f8 100644 --- a/autotest/test_gwf_vsc04_lak.py +++ b/autotest/test_gwf_vsc04_lak.py @@ -266,7 +266,10 @@ def build_model(idx, dir): gwf, viscref=viscref, viscosity_filerecord=vsc_filerecord, - viscosityfuncrecord=[("nonlinear", 10.0, 248.37, 133.15)], + thermal_formulation="nonlinear", + thermal_a2=10.0, + thermal_a3=248.37, + thermal_a4=133.16, nviscspecies=len(vsc_pd), packagedata=vsc_pd, pname="vsc", diff --git a/autotest/test_gwf_vsc05_hfb.py b/autotest/test_gwf_vsc05_hfb.py index db3d94600f0..a5dfcdeb8ac 100644 --- a/autotest/test_gwf_vsc05_hfb.py +++ b/autotest/test_gwf_vsc05_hfb.py @@ -155,7 +155,10 @@ def build_model(idx, dir): gwf, viscref=8.904e-4, viscosity_filerecord=vsc_filerecord, - viscosityfuncrecord=[("nonlinear", 10.0, 248.37, 133.16)], + thermal_formulation="nonlinear", + thermal_a2=10.0, + thermal_a3=248.37, + thermal_a4=133.16, nviscspecies=len(vsc_pd), packagedata=vsc_pd, pname="vsc", diff --git a/doc/mf6io/mf6ivar/dfn/gwf-vsc.dfn b/doc/mf6io/mf6ivar/dfn/gwf-vsc.dfn index 4633076b027..fa11bda5bff 100644 --- a/doc/mf6io/mf6ivar/dfn/gwf-vsc.dfn +++ b/doc/mf6io/mf6ivar/dfn/gwf-vsc.dfn @@ -9,6 +9,43 @@ longname reference viscosity description fluid reference viscosity used in the equation of state. This value is set to 1.0 if not specified as an option. default_value 1.0 +block options +name thermal_formulation +type string +shape +reader urword +optional true +valid linear nonlinear +longname keyword to specify viscosity formulation for the temperature species +description may be used for specifying which viscosity formulation to use for a species identified by the auxilary name TEMPERATURE. Can be either LINEAR or NONLINEAR. The LINEAR viscosity formulation is the default. + +block options +name thermal_a2 +type double +reader urword +optional true +longname coefficient used in nonlinear viscosity function +description is an empirical parameter specified by the user for calculating viscosity using a nonlinear formulation. If A2 is not specified, a default value of 10.0 is assigned (Voss, 1984). +default_value 10. + +block options +name thermal_a3 +type double +reader urword +optional true +longname coefficient used in nonlinear viscosity function +description is an empirical parameter specified by the user for calculating viscosity using a nonlinear formulation. If A3 is not specified, a default value of 248.37 is assigned (Voss, 1984). +default_value 248.37 + +block options +name thermal_a4 +type double precision +reader urword +optional true +longname coefficient used in nonlinear viscosity function +description is an empirical parameter specified by the user for calculating viscosity using a nonlinear formulation. If A4 is not specified, a default value of 133.15 is assigned (Voss, 1984). +default_value 133.15 + block options name viscosity_filerecord type record viscosity fileout viscosityfile @@ -53,63 +90,6 @@ optional false longname file keyword description name of the binary output file to write viscosity information. The viscosity file has the same format as the head file. Viscosity values will be written to the viscosity file whenever heads are written to the binary head file. The settings for controlling head output are contained in the Output Control option. -block options -name viscosityfuncrecord -type record thermal_viscosity_func formulation a2 a3 a4 -reader urword -optional true -longname -description - -block options -name thermal_viscosity_func -type keyword -shape -in_record true -tagged true -reader urword -optional false -longname keyword to specify viscosity formulation for the temperature species -description may be used for specifying which viscosity formulation to use for a species identified by the auxilary name TEMPERATURE. Can be either LINEAR or NONLINEAR. The LINEAR viscosity formulation is the default. - -block options -name formulation -type string -reader urword -optional false -valid linear nonlinear -longname keyword to specify viscosity formulation -description may be used for specifying which viscosity formulation to use. The linear viscosity formulation is the default. - - -block options -name a2 -type double precision -in_record true -reader urword -optional true -longname coefficient used in nonlinear viscosity function -description is an empirical parameter specified by the user for calculating viscosity using a nonlinear formulation. If A2 is not specified, a default value of 10.0 is assigned (Voss, 1984). - -block options -name a3 -type double precision -in_record true -reader urword -optional true -longname coefficient used in nonlinear viscosity function -description is an empirical parameter specified by the user for calculating viscosity using a nonlinear formulation. If A3 is not specified, a default value of 248.37 is assigned (Voss, 1984). - -block options -name a4 -type double precision -in_record true -reader urword -optional true -longname coefficient used in nonlinear viscosity function -description is an empirical parameter specified by the user for calculating viscosity using a nonlinear formulation. If A4 is not specified, a default value of 133.15 is assigned (Voss, 1984). - - # --------------------- gwf vsc dimensions --------------------- diff --git a/src/Model/GroundWaterFlow/gwf3vsc8.f90 b/src/Model/GroundWaterFlow/gwf3vsc8.f90 index da1aa515f53..9d2ea921853 100644 --- a/src/Model/GroundWaterFlow/gwf3vsc8.f90 +++ b/src/Model/GroundWaterFlow/gwf3vsc8.f90 @@ -1260,7 +1260,7 @@ subroutine allocate_scalars(this) ! SPECIFICATIONS: ! ------------------------------------------------------------------------------ ! -- modules - use ConstantsModule, only: DZERO + use ConstantsModule, only: DZERO, DTEN, DEP3 ! -- dummy class(GwfVscType) :: this ! -- local @@ -1288,10 +1288,10 @@ subroutine allocate_scalars(this) this%ioutvisc = 0 this%ireadelev = 0 this%iconcset = 0 - this%viscref = 1000.d0 - this%A2 = DZERO - this%A3 = DZERO - this%A4 = DZERO + this%viscref = DEP3 + this%A2 = DTEN + this%A3 = 248.37_DP + this%A4 = 133.15_DP ! this%nviscspecies = 0 ! @@ -1405,7 +1405,7 @@ subroutine read_options(this) 'followed by FILEOUT' call store_error(errmsg) end if - case ('THERMAL_VISCOSITY_FUNC') + case ('THERMAL_FORMULATION') call this%parser%GetStringCaps(keyword2) if (trim(adjustl(keyword2)) == 'LINEAR') this%thermivisc = 1 if (trim(adjustl(keyword2)) == 'NONLINEAR') this%thermivisc = 2 @@ -1414,25 +1414,40 @@ subroutine read_options(this) write (this%iout, fmtlinear) case (2) write (this%iout, fmtnonlinear) - this%a2 = this%parser%GetDouble() - this%a3 = this%parser%GetDouble() - this%a4 = this%parser%GetDouble() - ! - ! -- Write viscosity function selection to lst file - write (this%iout, '(/,1x,a,a,a)') 'Constants used in ', & - trim(keyword2), ' viscosity formulation are ' - write (this%iout, '(1x,a)') & - ' A2, A3, A4' - line = ' ' - write (c16, '(g15.6)') this%a2 - line = trim(line)//' '//adjustr(c16) - write (c16, '(g15.6)') this%a3 - line = trim(line)//' '//adjustr(c16) - write (c16, '(g15.6)') this%a4 - line = trim(line)//' '//adjustr(c16) - write (this%iout, '(a)') trim(line) - end select + case ('THERMAL_A2') + this%a2 = this%parser%GetDouble() + if (this%thermivisc == 2) then + write (this%iout, '(4x,a,1pg15.6)') & + 'A2 in nonlinear viscosity formulation has been set to: ', & + this%a2 + else + write (this%iout, '(4x,a,/,4x,a,/,4x,a)') 'THERMAL_A2 specified by user & + &in VSC Package input file. LINEAR viscosity ', 'formulation also & + &specified. THERMAL_A2 will not affect ', 'viscosity calculations.' + end if + case ('THERMAL_A3') + this%a3 = this%parser%GetDouble() + if (this%thermivisc == 2) then + write (this%iout, '(4x,a,1pg15.6)') & + 'A3 in nonlinear viscosity formulation has been set to: ', & + this%a3 + else + write (this%iout, '(4x,a,/,4x,a,/,4x,a)') 'THERMAL_A3 specified by user & + &in VSC Package input file. LINEAR viscosity ', 'formulation also & + &specified. THERMAL_A3 will not affect ', 'viscosity calculations.' + end if + case ('THERMAL_A4') + this%a4 = this%parser%GetDouble() + if (this%thermivisc == 2) then + write (this%iout, '(4x,a,1pg15.6)') & + 'A4 in nonlinear viscosity formulation has been set to: ', & + this%a4 + else + write (this%iout, '(4x,a,/,4x,a,/,4x,a)') 'THERMAL_A4 specified by user & + &in VSC Package input file. LINEAR viscosity ', 'formulation also & + &specified. THERMAL_A4 will not affect ', 'viscosity calculations.' + end if case default write (errmsg, '(4x,a,a)') '**Error. Unknown VSC option: ', & trim(keyword) From e6a19c06802e641aca020bd62e5aae2caa3d7fbc Mon Sep 17 00:00:00 2001 From: Eric Morway Date: Tue, 8 Nov 2022 04:38:59 -0800 Subject: [PATCH 24/31] Remove unused variables --- src/Model/GroundWaterFlow/gwf3vsc8.f90 | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Model/GroundWaterFlow/gwf3vsc8.f90 b/src/Model/GroundWaterFlow/gwf3vsc8.f90 index 9d2ea921853..e9c91ba47a1 100644 --- a/src/Model/GroundWaterFlow/gwf3vsc8.f90 +++ b/src/Model/GroundWaterFlow/gwf3vsc8.f90 @@ -1358,8 +1358,6 @@ subroutine read_options(this) ! -- local character(len=LINELENGTH) :: warnmsg, errmsg, keyword, keyword2 character(len=MAXCHARLEN) :: fname - character(len=LINELENGTH) :: line - character(len=16) :: c16 integer(I4B) :: ierr logical :: isfound, endOfBlock ! -- formats From f23ddbca0f8660de3ca48885a6b901786656c64a Mon Sep 17 00:00:00 2001 From: Eric Morway Date: Tue, 8 Nov 2022 16:53:31 -0800 Subject: [PATCH 25/31] Resolve releaseNotes conflict, restore HFB condsat_modify function --- doc/ReleaseNotes/ReleaseNotes.tex | 3 ++- src/Model/GroundWaterFlow/gwf3hfb8.f90 | 12 ++---------- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/doc/ReleaseNotes/ReleaseNotes.tex b/doc/ReleaseNotes/ReleaseNotes.tex index 67d6c430c94..9d8aa6dafdb 100644 --- a/doc/ReleaseNotes/ReleaseNotes.tex +++ b/doc/ReleaseNotes/ReleaseNotes.tex @@ -199,7 +199,7 @@ \section{Changes Introduced in this Release} \underline{EXAMPLES} \begin{itemize} - % \item xxx + \item A new example called ex-gwt-stallman was added. This new problem uses the GWT Model as a surrogate for simulating heat flow. The example represents heat conduction in subsurface with a periodic temperature boundary condition at the surface. % \item xxx % \item xxx \end{itemize} @@ -213,6 +213,7 @@ \section{Changes Introduced in this Release} \item When a GWF Model and a corresponding GWT model are solved in the same simulation, the GWF Model must be solved before the corresponding GWT model. The GWF Model must also be solved by a different IMS than the GWT Model. There was not a check for this in previous versions and if these conditions were not met, the solution would often not converge or it would give erroneous results. \item The DISV Package would not raise an error if a model cell was defined as a line. The program was modified to check for the case where the calculated cell area is equal to zero. If the calculated cell area is equal to zero, the program terminates with an error. \item When searching for a required block in an input file, the program would not terminate with a sensible error message if the end of file was found instead of the required block. Program now indicates that the required block was not found. + \item This release contains a first step toward implementation of generic input routines to read input files. The new input routines were implemented for the DIS, DISV, and DISU Packages of the GWF and GWT Models, for the NPF Package of the GWF Model, and the DSP Package of the GWT Model. Output summaries written to the GWF and GWT Model listing files are different from summaries written using previous versions. For packages that use the new input data model, the IPRN capability of the READARRAY utility (described in mf6io.pdf) is no longer supported as a way to write input arrays to the model listing file. \end{itemize} \underline{INTERNAL FLOW PACKAGES} diff --git a/src/Model/GroundWaterFlow/gwf3hfb8.f90 b/src/Model/GroundWaterFlow/gwf3hfb8.f90 index 52c4841a9d6..ffec99e84bd 100644 --- a/src/Model/GroundWaterFlow/gwf3hfb8.f90 +++ b/src/Model/GroundWaterFlow/gwf3hfb8.f90 @@ -892,7 +892,7 @@ subroutine condsat_modify(this) ! SPECIFICATIONS: ! ------------------------------------------------------------------------------ ! -- modules - use ConstantsModule, only: DHALF, DZERO, DONE + use ConstantsModule, only: DHALF, DZERO ! -- dummy class(GwfHfbType) :: this ! -- local @@ -901,22 +901,14 @@ subroutine condsat_modify(this) real(DP) :: cond, condhfb real(DP) :: fawidth, faheight real(DP) :: topn, topm, botn, botm - real(DP) :: viscratio ! ------------------------------------------------------------------------------ ! do ihfb = 1, this%nhfb - ! -- initialize variable - viscratio = DONE - ! ipos = this%idxloc(ihfb) cond = this%condsat(this%jas(ipos)) this%csatsav(ihfb) = cond n = this%noden(ihfb) m = this%nodem(ihfb) - ! -- account for viscosity when active - if (this%ivsc /= 0) then -! call this%get_visc_ratio(n, m, hnew(n), hnew(m), viscratio) - end if ! if (this%inewton == 1 .or. & (this%icelltype(n) == 0 .and. this%icelltype(m) == 0)) then @@ -933,7 +925,7 @@ subroutine condsat_modify(this) end if if (this%hydchr(ihfb) > DZERO) then fawidth = this%hwva(this%jas(ipos)) - condhfb = this%hydchr(ihfb) * viscratio * & + condhfb = this%hydchr(ihfb) * & fawidth * faheight cond = cond * condhfb / (cond + condhfb) else From eeeb09b47e500eb956326da020aeb123b62c4ff8 Mon Sep 17 00:00:00 2001 From: Eric Morway Date: Wed, 9 Nov 2022 03:50:47 -0800 Subject: [PATCH 26/31] After redoing vsc.dfn, update associated tex files --- doc/mf6io/mf6ivar/examples/gwf-vsc-example.dat | 5 ++++- doc/mf6io/mf6ivar/md/mf6ivar.md | 9 ++++----- doc/mf6io/mf6ivar/tex/gwf-vsc-desc.tex | 16 +++++++--------- doc/mf6io/mf6ivar/tex/gwf-vsc-options.dat | 6 ++++-- 4 files changed, 19 insertions(+), 17 deletions(-) diff --git a/doc/mf6io/mf6ivar/examples/gwf-vsc-example.dat b/doc/mf6io/mf6ivar/examples/gwf-vsc-example.dat index ace2f4cb7c3..48d21bd801b 100644 --- a/doc/mf6io/mf6ivar/examples/gwf-vsc-example.dat +++ b/doc/mf6io/mf6ivar/examples/gwf-vsc-example.dat @@ -1,7 +1,10 @@ BEGIN OPTIONS VISCREF 8.904E-04 + THERMAL_FORMULATION NONLINEAR + THERMAL_A2 10.0 + THERMAL_A3 248.37 + THERMAL_A4 133.15 VISCOSITY FILEOUT GWF-VSC.vsc.bin - THERMAL_VISCOSITY_FUNC NONLINEAR 10.0 248.37 133.15 END OPTIONS BEGIN DIMENSIONS diff --git a/doc/mf6io/mf6ivar/md/mf6ivar.md b/doc/mf6io/mf6ivar/md/mf6ivar.md index 685a1ed5e7b..61613c55a15 100644 --- a/doc/mf6io/mf6ivar/md/mf6ivar.md +++ b/doc/mf6io/mf6ivar/md/mf6ivar.md @@ -792,14 +792,13 @@ | GWF | OC | PERIOD | FREQUENCY | INTEGER | save at the specified time step frequency. This keyword may be used in conjunction with other keywords to print or save results for multiple time steps. | | GWF | OC | PERIOD | STEPS | INTEGER (] + [THERMAL_FORMULATION ] + [THERMAL_A2 ] + [THERMAL_A3 ] + [THERMAL_A4 ] [VISCOSITY FILEOUT ] - [THERMAL_VISCOSITY_FUNC FORMULATION [A2 ] [A3 ] [A4 ]] - FORMULATION END OPTIONS From 98212c17a3dd44fd568252be58607f986c82206f Mon Sep 17 00:00:00 2001 From: Eric Morway Date: Thu, 10 Nov 2022 15:06:44 -0800 Subject: [PATCH 27/31] Backup K arrays should initially be allocated size zero and reallocated to size ncells when VSC is active --- src/Model/GroundWaterFlow/gwf3npf8.f90 | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/Model/GroundWaterFlow/gwf3npf8.f90 b/src/Model/GroundWaterFlow/gwf3npf8.f90 index 0ad9fd70bc0..c6de60eed60 100644 --- a/src/Model/GroundWaterFlow/gwf3npf8.f90 +++ b/src/Model/GroundWaterFlow/gwf3npf8.f90 @@ -366,10 +366,14 @@ subroutine npf_ar(this, ic, vsc, ibound, hnew) ! ! -- allocate arrays to store original user input in case TVK/VSC modify them if (this%invsc > 0) then - ! Need to allocate arrays that will store the original K values so - ! that the current K11 etc. carry the "real" K's that are updated by the - ! vscratio. This approach leverages existing functionality that makes use - ! of K. + ! + ! -- Reallocate arrays that store user-input values. + call mem_reallocate(this%k11input, this%dis%nodes, 'K11INPUT', this%memoryPath) + call mem_reallocate(this%k22input, this%dis%nodes, 'K22INPUT', this%memoryPath) + call mem_reallocate(this%k33input, this%dis%nodes, 'K33INPUT', this%memoryPath) + ! Allocate arrays that will store the original K values. When VSC active, + ! the current Kxx arrays carry the viscosity-adjusted K values. + ! This approach leverages existing functionality that makes use of K. call this%store_original_k_arrays(this%dis%nodes, this%dis%njas) end if ! @@ -1274,7 +1278,7 @@ subroutine allocate_scalars(this) return end subroutine allocate_scalars - !> @ brief Stores a backup copy of hydraulic conductivity when the VSC + !> @ brief Store backup copy of hydraulic conductivity when the VSC !! package is activate !! !! The K arrays (K11, etc.) get multiplied by the viscosity ratio @@ -1334,9 +1338,6 @@ subroutine allocate_arrays(this, ncells, njas) ! -- Optional arrays dimensioned to full size initially call mem_allocate(this%k22, ncells, 'K22', this%memoryPath) call mem_allocate(this%k33, ncells, 'K33', this%memoryPath) - call mem_allocate(this%k11input, ncells, 'K11INPUT', this%memoryPath) - call mem_allocate(this%k22input, ncells, 'K22INPUT', this%memoryPath) - call mem_allocate(this%k33input, ncells, 'K33INPUT', this%memoryPath) call mem_allocate(this%wetdry, ncells, 'WETDRY', this%memoryPath) call mem_allocate(this%angle1, ncells, 'ANGLE1', this%memoryPath) call mem_allocate(this%angle2, ncells, 'ANGLE2', this%memoryPath) @@ -1348,6 +1349,11 @@ subroutine allocate_arrays(this, ncells, njas) call mem_allocate(this%ihcedge, 0, 'IHCEDGE', this%memoryPath) call mem_allocate(this%propsedge, 0, 0, 'PROPSEDGE', this%memoryPath) ! + ! -- Optional arrays only needed when vsc package is active + call mem_allocate(this%k11input, 0, 'K11INPUT', this%memoryPath) + call mem_allocate(this%k22input, 0, 'K22INPUT', this%memoryPath) + call mem_allocate(this%k33input, 0, 'K33INPUT', this%memoryPath) + ! ! -- Specific discharge is (re-)allocated when nedges is known call mem_allocate(this%spdis, 3, 0, 'SPDIS', this%memoryPath) ! From 112b6fc93e3a1ea57fa428ce40603593bafac5e2 Mon Sep 17 00:00:00 2001 From: Eric Morway Date: Thu, 10 Nov 2022 15:16:08 -0800 Subject: [PATCH 28/31] fprettify --- src/Model/GroundWaterFlow/gwf3npf8.f90 | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Model/GroundWaterFlow/gwf3npf8.f90 b/src/Model/GroundWaterFlow/gwf3npf8.f90 index c6de60eed60..889c49e3fc9 100644 --- a/src/Model/GroundWaterFlow/gwf3npf8.f90 +++ b/src/Model/GroundWaterFlow/gwf3npf8.f90 @@ -368,9 +368,12 @@ subroutine npf_ar(this, ic, vsc, ibound, hnew) if (this%invsc > 0) then ! ! -- Reallocate arrays that store user-input values. - call mem_reallocate(this%k11input, this%dis%nodes, 'K11INPUT', this%memoryPath) - call mem_reallocate(this%k22input, this%dis%nodes, 'K22INPUT', this%memoryPath) - call mem_reallocate(this%k33input, this%dis%nodes, 'K33INPUT', this%memoryPath) + call mem_reallocate(this%k11input, this%dis%nodes, 'K11INPUT', & + this%memoryPath) + call mem_reallocate(this%k22input, this%dis%nodes, 'K22INPUT', & + this%memoryPath) + call mem_reallocate(this%k33input, this%dis%nodes, 'K33INPUT', & + this%memoryPath) ! Allocate arrays that will store the original K values. When VSC active, ! the current Kxx arrays carry the viscosity-adjusted K values. ! This approach leverages existing functionality that makes use of K. From 0fdc07136a6a889d0f220d5feb2f20958ea2ed97 Mon Sep 17 00:00:00 2001 From: Eric Morway Date: Mon, 14 Nov 2022 13:35:50 -0800 Subject: [PATCH 29/31] Reconfigure how vsc autotests are verified. Relative check of sim results now compared to established solution instead of against each other. Reran black. --- autotest/test_gwf_vsc01.py | 70 +++++++--------- autotest/test_gwf_vsc02.py | 68 ++++++--------- autotest/test_gwf_vsc03_sfr.py | 93 +++++++++++++++------ autotest/test_gwf_vsc04_lak.py | 146 +++++++++++++++++++++++++-------- autotest/test_gwf_vsc05_hfb.py | 91 ++++++++------------ 5 files changed, 271 insertions(+), 197 deletions(-) diff --git a/autotest/test_gwf_vsc01.py b/autotest/test_gwf_vsc01.py index ac89615c81c..0c547db2e77 100644 --- a/autotest/test_gwf_vsc01.py +++ b/autotest/test_gwf_vsc01.py @@ -277,62 +277,48 @@ def eval_results(sim): budobj = flopy.utils.CellBudgetFile(fname, precision="double") outbud = budobj.get_data(text=" GHB") + # Establish known answer: + stored_ans = -151.63446156218242 + if sim.idxsim == 0: no_vsc_bud_last = np.array(outbud[-1].tolist()) - np.savetxt( - os.path.join(os.path.dirname(exdirs[sim.idxsim]), "mod1reslt.txt"), - no_vsc_bud_last, + sim_val_1 = no_vsc_bud_last[:, 2].sum() + + # Ensure latest simulated value hasn't changed from stored answer + assert np.allclose( + sim_val_1, stored_ans, atol=1e-4 + ), "Flow in the " + exdirs[ + 0 + ] + " test problem (doesn't simulate " "viscosity) has changed,\n should be " + str( + stored_ans + ) + " but instead is " + str( + sim_val_1 ) elif sim.idxsim == 1: with_vsc_bud_last = np.array(outbud[-1].tolist()) - np.savetxt( - os.path.join(os.path.dirname(exdirs[sim.idxsim]), "mod2reslt.txt"), - with_vsc_bud_last, + sim_val_2 = with_vsc_bud_last[:, 2].sum() + + # Ensure latest simulated value hasn't changed from stored answer + assert np.allclose( + sim_val_2, stored_ans, atol=1e-4 + ), "Flow in the " + exdirs[ + 1 + ] + " test problem (simulates " "viscosity) has changed,\n should be " + str( + stored_ans + ) + " but instead is " + str( + sim_val_2 ) elif sim.idxsim == 2: no_vsc_low_k_bud_last = np.array(outbud[-1].tolist()) - np.savetxt( - os.path.join(os.path.dirname(exdirs[sim.idxsim]), "mod3reslt.txt"), - no_vsc_low_k_bud_last, - ) - - # if all 3 models have run, check relative results - if sim.idxsim == 2: - f1 = os.path.join(os.path.dirname(exdirs[sim.idxsim]), "mod1reslt.txt") - if os.path.isfile(f1): - no_vsc_bud_last = np.loadtxt(f1) - os.remove(f1) - - f2 = os.path.join(os.path.dirname(exdirs[sim.idxsim]), "mod2reslt.txt") - if os.path.isfile(f2): - with_vsc_bud_last = np.loadtxt(f2) - os.remove(f2) - - f3 = os.path.join(os.path.dirname(exdirs[sim.idxsim]), "mod3reslt.txt") - if os.path.isfile(f3): - no_vsc_low_k_bud_last = np.loadtxt(f3) - os.remove(f3) - - model1_exit = no_vsc_bud_last[:, 2].sum() - model2_exit = with_vsc_bud_last[:, 2].sum() - model3_exit = no_vsc_low_k_bud_last[:, 2].sum() - - # Ensure models 1 & 2 give nearly identical results - assert np.allclose(model1_exit, model2_exit, atol=1e-3), ( - "Flow in models " - + exdirs[0] - + " and " - + exdirs[1] - + " should be equal, but are not." - ) + sim_val_3 = no_vsc_low_k_bud_last[:, 2].sum() # Ensure the flow leaving model 3 is less than what leaves model 2 - assert abs(model2_exit) > abs(model3_exit), ( + assert abs(stored_ans) > abs(sim_val_3), ( "Exit flow from model " + exdirs[1] - + " should be greater than flow existing " + + " should be greater than flow exiting " + exdirs[2] + ", but it is not." ) diff --git a/autotest/test_gwf_vsc02.py b/autotest/test_gwf_vsc02.py index 63eaccea970..2e6039d5116 100644 --- a/autotest/test_gwf_vsc02.py +++ b/autotest/test_gwf_vsc02.py @@ -281,59 +281,45 @@ def eval_results(sim): budobj = flopy.utils.CellBudgetFile(fname, precision="double") outbud = budobj.get_data(text=" GHB") + # Establish known answer: + stored_ans = 452.5316256451224 + if sim.idxsim == 0: no_vsc_bud_last = np.array(outbud[-1].tolist()) - np.savetxt( - os.path.join(os.path.dirname(exdirs[sim.idxsim]), "mod1reslt.txt"), - no_vsc_bud_last, + sim_val_1 = no_vsc_bud_last[:, 2].sum() + + # Ensure latest simulated value hasn't changed from stored answer + assert np.allclose( + sim_val_1, stored_ans, atol=1e-4 + ), "Flow in the " + exdirs[ + 0 + ] + " test problem (doesn't simulate " "viscosity) has changed,\n should be " + str( + stored_ans + ) + " but instead is " + str( + sim_val_1 ) elif sim.idxsim == 1: with_vsc_bud_last = np.array(outbud[-1].tolist()) - np.savetxt( - os.path.join(os.path.dirname(exdirs[sim.idxsim]), "mod2reslt.txt"), - with_vsc_bud_last, + sim_val_2 = with_vsc_bud_last[:, 2].sum() + + # Ensure latest simulated value hasn't changed from stored answer + assert np.allclose( + sim_val_2, stored_ans, atol=1e-4 + ), "Flow in the " + exdirs[ + 1 + ] + " test problem (simulates " "viscosity) has changed,\n should be " + str( + stored_ans + ) + " but instead is " + str( + sim_val_2 ) elif sim.idxsim == 2: no_vsc_low_k_bud_last = np.array(outbud[-1].tolist()) - np.savetxt( - os.path.join(os.path.dirname(exdirs[sim.idxsim]), "mod3reslt.txt"), - no_vsc_low_k_bud_last, - ) - - # if all 3 models have run, check relative results - if sim.idxsim == 2: - f1 = os.path.join(os.path.dirname(exdirs[sim.idxsim]), "mod1reslt.txt") - if os.path.isfile(f1): - no_vsc_bud_last = np.loadtxt(f1) - os.remove(f1) - - f2 = os.path.join(os.path.dirname(exdirs[sim.idxsim]), "mod2reslt.txt") - if os.path.isfile(f2): - with_vsc_bud_last = np.loadtxt(f2) - os.remove(f2) - - f3 = os.path.join(os.path.dirname(exdirs[sim.idxsim]), "mod3reslt.txt") - if os.path.isfile(f3): - no_vsc_low_k_bud_last = np.loadtxt(f3) - os.remove(f3) - - model1_exit = no_vsc_bud_last[:, 2].sum() - model2_exit = with_vsc_bud_last[:, 2].sum() - model3_exit = no_vsc_low_k_bud_last[:, 2].sum() - - # Ensure models 1 & 2 give nearly identical results - assert np.allclose(model1_exit, model2_exit, atol=1e-3), ( - "Flow in models " - + exdirs[0] - + " and " - + exdirs[1] - + " should be equal, but are not." - ) + sim_val_3 = no_vsc_low_k_bud_last[:, 2].sum() # Ensure the flow leaving model 3 is less than what leaves model 2 - assert abs(model2_exit) > abs(model3_exit), ( + assert abs(stored_ans) > abs(sim_val_3), ( "Exit flow from model " + exdirs[1] + " should be greater than flow existing " diff --git a/autotest/test_gwf_vsc03_sfr.py b/autotest/test_gwf_vsc03_sfr.py index 001b7dbebb3..a3c79f0620b 100644 --- a/autotest/test_gwf_vsc03_sfr.py +++ b/autotest/test_gwf_vsc03_sfr.py @@ -458,44 +458,87 @@ def eval_results(sim): budobj = flopy.utils.CellBudgetFile(fname, precision="double") outbud = budobj.get_data(text=" GWF") + # Establish known answer: + stored_ans_up = np.array( + [ + -381.07448455, + -380.78732368, + -380.49858508, + -380.20823421, + -379.91623521, + -379.62255085, + -379.32714247, + -379.02996987, + -378.73099123, + -378.43016302, + ] + ) + + stored_ans_dn = np.array( + [ + 87.34154005, + 92.49173569, + 98.11443969, + 104.34176415, + 111.36392723, + 119.46886139, + 129.12349417, + 141.16795665, + 157.38797791, + 182.65575269, + ] + ) + if sim.idxsim == 0: + # convert np.array to list no_vsc_bud_last = np.array(outbud[-1].tolist()) - np.savetxt( - os.path.join(os.path.dirname(exdirs[sim.idxsim]), "mod1reslt.txt"), - no_vsc_bud_last, - ) - elif sim.idxsim == 1: - with_vsc_bud_last = np.array(outbud[-1].tolist()) - np.savetxt( - os.path.join(os.path.dirname(exdirs[sim.idxsim]), "mod2reslt.txt"), - with_vsc_bud_last, - ) + # sum up total losses and total gains in the first 10 reaches + # and the last 10 reaches + for i in np.arange(10): + # upper reaches + assert np.allclose( + abs(no_vsc_bud_last[i, 2]), abs(stored_ans_up[i]), atol=1e-4 + ), ( + "GW/SW not as expected in upper reaches of viscosity test " + "problem that uses SFR. This test keeps viscosity inactive " + "prior to activating VSC Package in next variant of this test " + "problem." + ) - # if both models have run, check relative results - if sim.idxsim == 1: - f1 = os.path.join(os.path.dirname(exdirs[sim.idxsim]), "mod1reslt.txt") - if os.path.isfile(f1): - no_vsc_bud_last = np.loadtxt(f1) - os.remove(f1) + # lower reaches + assert np.allclose( + abs(no_vsc_bud_last[-(i + 1), 2]), + abs(stored_ans_dn[-(i + 1)]), + atol=1e-4, + ), ( + "GW/SW not as expected in lower reaches of viscosity test " + "problem that uses SFR. This test keeps viscosity inactive " + "prior to activating VSC Package in next variant of this test " + " problem." + ) - f2 = os.path.join(os.path.dirname(exdirs[sim.idxsim]), "mod2reslt.txt") - if os.path.isfile(f2): - with_vsc_bud_last = np.loadtxt(f2) - os.remove(f2) + elif sim.idxsim == 1: + with_vsc_bud_last = np.array(outbud[-1].tolist()) # sum up total losses and total gains in the first 10 reaches # and the last 10 reaches for i in np.arange(10): # upper reaches - assert abs(no_vsc_bud_last[i, 2]) > abs( - with_vsc_bud_last[i, 2] - ), "GW/SW not as expected in upper reaches of viscosity w/ sfr example" + assert abs(stored_ans_up[i]) > abs(with_vsc_bud_last[i, 2]), ( + "GW/SW not as expected in upper reaches of viscosity test " + "problem that uses SFR. This test activates the VSC package that " + "should elicit a known relative change in the GW/SW exchange" + ) # lower reaches - assert abs(no_vsc_bud_last[-(i + 1), 2]) < abs( + assert abs(stored_ans_dn[-(i + 1)]) < abs( with_vsc_bud_last[-(i + 1), 2] - ), "GW/SW not as expected in lower reaches of viscosity w/ sfr example" + ), ( + "GW/SW not as expected in lower reaches of viscosity test " + "problem that uses SFR. This test activates the VSC package that " + "should elicit a known relative change in the GW/SW exchange" + ) # - No need to change any code below diff --git a/autotest/test_gwf_vsc04_lak.py b/autotest/test_gwf_vsc04_lak.py index 5296ff729f8..6978fc0df7a 100644 --- a/autotest/test_gwf_vsc04_lak.py +++ b/autotest/test_gwf_vsc04_lak.py @@ -475,7 +475,7 @@ def build_model(idx, dir): ) # pull in the tabfile defining the lake stage, vol, & surface area - fname = os.path.join("data", "vsc04-laktab", "stg-vol-surfarea.dat") + fname = os.path.join("data", "vsc04-laktab", "stg-vol-surfarea.csv") tabinput = [] with open(fname, "r") as f: # peel off the hdr line @@ -640,55 +640,133 @@ def eval_results(sim): budobj = flopy.utils.CellBudgetFile(fname, precision="double") outbud = budobj.get_data(text=" GWF") + # Establish known answer: + stored_ans = np.array( + [ + [1.0, 9.20e1, 7.34424130e-01, 1.15181189e06], + [1.0, 1.08e2, 2.44117249e00, 1.15181189e06], + [1.0, 3.98e2, 2.19216490e01, 1.15181189e06], + [1.0, 9.30e1, -6.78268488e-02, 1.15181189e06], + [1.0, 3.99e2, 3.17042406e-01, 1.15181189e06], + [1.0, 9.40e1, -7.47709994e-01, 1.15181189e06], + [1.0, 4.00e2, -6.88938281e00, 1.15181189e06], + [1.0, 9.50e1, -1.51336530e00, 1.15181189e06], + [1.0, 4.01e2, -1.57614834e01, 1.15181189e06], + [1.0, 9.60e1, -2.54095715e00, 1.15181189e06], + [1.0, 1.14e2, -3.95961161e00, 1.15181189e06], + [1.0, 4.02e2, -5.60853100e01, 1.15181189e06], + [1.0, 1.25e2, 2.35138538e00, 1.15181189e06], + [1.0, 4.15e2, 1.69275311e01, 1.15181189e06], + [1.0, 1.31e2, -3.66648779e00, 1.15181189e06], + [1.0, 4.19e2, -3.45225854e01, 1.15181189e06], + [1.0, 1.42e2, 2.32550672e00, 1.15181189e06], + [1.0, 4.32e2, 1.65405908e01, 1.15181189e06], + [1.0, 1.48e2, -3.58087615e00, 1.15181189e06], + [1.0, 4.36e2, -3.27154545e01, 1.15181189e06], + [1.0, 1.59e2, 2.35138505e00, 1.15181189e06], + [1.0, 4.49e2, 1.69275277e01, 1.15181189e06], + [1.0, 1.65e2, -3.66648777e00, 1.15181189e06], + [1.0, 4.53e2, -3.45225840e01, 1.15181189e06], + [1.0, 1.76e2, 2.44117266e00, 1.15181189e06], + [1.0, 1.94e2, 7.34424819e-01, 1.15181189e06], + [1.0, 4.66e2, 2.19216459e01, 1.15181189e06], + [1.0, 1.95e2, -6.78264346e-02, 1.15181189e06], + [1.0, 4.67e2, 3.17038149e-01, 1.15181189e06], + [1.0, 1.96e2, -7.47709250e-01, 1.15181189e06], + [1.0, 4.68e2, -6.88938656e00, 1.15181189e06], + [1.0, 1.97e2, -1.51336458e00, 1.15181189e06], + [1.0, 4.69e2, -1.57614826e01, 1.15181189e06], + [1.0, 1.82e2, -3.95961151e00, 1.15181189e06], + [1.0, 1.98e2, -2.54095654e00, 1.15181189e06], + [1.0, 4.70e2, -5.60853022e01, 1.15181189e06], + [1.0, 3.99e2, 4.03508517e-03, 1.15181189e06], + [1.0, 4.15e2, 2.15441304e-01, 1.15181189e06], + [1.0, 7.05e2, 8.25215117e-01, 1.15181189e06], + [1.0, 4.00e2, -8.76830539e-02, 1.15181189e06], + [1.0, 7.06e2, -3.90793309e-01, 1.15181189e06], + [1.0, 4.01e2, -2.00600698e-01, 1.15181189e06], + [1.0, 4.19e2, -4.39378360e-01, 1.15181189e06], + [1.0, 7.07e2, -2.43955302e00, 1.15181189e06], + [1.0, 4.32e2, 2.10516610e-01, 1.15181189e06], + [1.0, 7.22e2, 8.37390920e-01, 1.15181189e06], + [1.0, 7.23e2, -6.85716153e-02, 1.15181189e06], + [1.0, 4.36e2, -4.16378511e-01, 1.15181189e06], + [1.0, 7.24e2, -1.73090130e00, 1.15181189e06], + [1.0, 4.49e2, 2.15441262e-01, 1.15181189e06], + [1.0, 4.67e2, 4.03503099e-03, 1.15181189e06], + [1.0, 7.39e2, 8.25212943e-01, 1.15181189e06], + [1.0, 4.68e2, -8.76831016e-02, 1.15181189e06], + [1.0, 7.40e2, -3.90795105e-01, 1.15181189e06], + [1.0, 4.53e2, -4.39378341e-01, 1.15181189e06], + [1.0, 4.69e2, -2.00600688e-01, 1.15181189e06], + [1.0, 7.41e2, -2.43955388e00, 1.15181189e06], + ] + ) + + # talley some flows on the left and right sides of the lake for comparison + # test + left_chk_ans = [] + right_chk_ans = [] + left_chk_no_vsc = [] + right_chk_no_vsc = [] + left_chk_with_vsc = [] + right_chk_with_vsc = [] + if sim.idxsim == 0: no_vsc_bud_last = np.array(outbud[-1].tolist()) - np.savetxt( - os.path.join(os.path.dirname(exdirs[sim.idxsim]), "mod1reslt.txt"), - no_vsc_bud_last, - ) + no_vsc_bud_np = np.array(no_vsc_bud_last.tolist()) - elif sim.idxsim == 1: - with_vsc_bud_last = np.array(outbud[-1].tolist()) - np.savetxt( - os.path.join(os.path.dirname(exdirs[sim.idxsim]), "mod2reslt.txt"), - with_vsc_bud_last, - ) + for idx in np.arange(stored_ans.shape[0]): + k, i, j = lak_lkup_dict[idx] - # if both models have run, check relative results - if sim.idxsim == 1: - f1 = os.path.join(os.path.dirname(exdirs[0]), "mod1reslt.txt") - if os.path.isfile(f1): - no_vsc_bud_last = np.loadtxt(f1) - os.remove(f1) + # left side of lake + if j < 7: + if no_vsc_bud_np[idx, 2] > 0 and stored_ans[idx, 2] > 0: + left_chk_no_vsc.append(no_vsc_bud_np[idx, 2]) + left_chk_ans.append(stored_ans[idx, 2]) - f2 = os.path.join(os.path.dirname(exdirs[1]), "mod2reslt.txt") - if os.path.isfile(f2): - with_vsc_bud_last = np.loadtxt(f2) - os.remove(f2) + # right side of lake + if j > 9: + if no_vsc_bud_np[idx, 2] < 0 and stored_ans[idx, 2] < 0: + right_chk_no_vsc.append(no_vsc_bud_np[idx, 2]) + right_chk_ans.append(stored_ans[idx, 2]) - # talley some flows on the left and right sides of the lake for comparison - # test - no_vsc_bud_np = np.array(no_vsc_bud_last.tolist()) - with_vsc_bud_np = np.array(with_vsc_bud_last.tolist()) + # Check that all the flows entering the lak in the 'with vsc' model are greater + # than their 'no vsc' counterpart + assert np.allclose( + np.array(left_chk_ans), np.array(left_chk_no_vsc), atol=1e-3 + ), ( + "Lake inflow in no-VSC LAK simulation do not match established " + "solution." + ) - left_chk_no_vsc = [] - right_chk_no_vsc = [] - left_chk_with_vsc = [] - right_chk_with_vsc = [] + # Check that all the flows leaving the lak in the 'with vsc' model are less + # than their 'no vsc' counterpart (keep in mind values are negative, which + # affects how the comparison is made) + assert np.allclose( + np.array(right_chk_ans), np.array(right_chk_no_vsc), atol=1e-3 + ), ( + "Lake outflow in no-VSC LAK simulation do not match established " + "solution." + ) - for idx in np.arange(no_vsc_bud_np.shape[0]): + elif sim.idxsim == 1: + with_vsc_bud_last = np.array(outbud[-1].tolist()) + with_vsc_bud_np = np.array(with_vsc_bud_last.tolist()) + + for idx in np.arange(stored_ans.shape[0]): k, i, j = lak_lkup_dict[idx] # left side of lake if j < 7: - if no_vsc_bud_np[idx, 2] > 0 and with_vsc_bud_np[idx, 2] > 0: - left_chk_no_vsc.append(no_vsc_bud_np[idx, 2]) + if stored_ans[idx, 2] > 0 and with_vsc_bud_np[idx, 2] > 0: + left_chk_no_vsc.append(stored_ans[idx, 2]) left_chk_with_vsc.append(with_vsc_bud_np[idx, 2]) # right side of lake if j > 9: - if no_vsc_bud_np[idx, 2] < 0 and with_vsc_bud_np[idx, 2] < 0: - right_chk_no_vsc.append(no_vsc_bud_np[idx, 2]) + if stored_ans[idx, 2] < 0 and with_vsc_bud_np[idx, 2] < 0: + right_chk_no_vsc.append(stored_ans[idx, 2]) right_chk_with_vsc.append(with_vsc_bud_np[idx, 2]) # Check that all the flows entering the lak in the 'with vsc' model are greater diff --git a/autotest/test_gwf_vsc05_hfb.py b/autotest/test_gwf_vsc05_hfb.py index a5dfcdeb8ac..6846226fd78 100644 --- a/autotest/test_gwf_vsc05_hfb.py +++ b/autotest/test_gwf_vsc05_hfb.py @@ -313,6 +313,22 @@ def eval_results(sim): budobj = flopy.utils.CellBudgetFile(fname, precision="double") outbud = budobj.get_data(text=" FLOW-JA-FACE")[-1].squeeze() + # Establish known answer for the "with viscosity" variant: + stored_ans = np.array( + [ + [4, 5, 131.03196884892344], + [14, 15, 133.1834658429856], + [24, 25, 139.31716925610493], + [34, 35, 156.14497435040056], + [44, 45, 209.1055337693415], + [54, 55, 36.91267872240113], + [64, 65, 46.16474722642168], + [74, 75, 51.2708505192076], + [84, 85, 54.04369740428511], + [94, 95, 55.27469944201896], + ] + ) + # Look at flow entering the left face for the cells in the 6th (1-based) column cells = [gwf.modelgrid.get_node([(0, i, 5)])[0] for i in np.arange(nrow)] @@ -327,73 +343,38 @@ def eval_results(sim): if sim.idxsim == 0: no_vsc_bud_last = np.array(vals_to_store) - np.savetxt( - os.path.join(os.path.dirname(exdirs[sim.idxsim]), "mod1reslt.txt"), - no_vsc_bud_last, + + # Ensure with and without VSC simulations give nearly identical flow results + # for each cell-to-cell exchange between columns 5 and 6 + assert np.allclose( + no_vsc_bud_last[:, 2], stored_ans[:, 2], atol=1e-3 + ), ( + "Flow in models " + + exdirs[0] + + " and the established answer should be approximately " + "equal, but are not." ) elif sim.idxsim == 1: with_vsc_bud_last = np.array(vals_to_store) - np.savetxt( - os.path.join(os.path.dirname(exdirs[sim.idxsim]), "mod2reslt.txt"), - with_vsc_bud_last, - ) - - elif sim.idxsim == 2: - no_vsc_low_k_bud_last = np.array(vals_to_store) - np.savetxt( - os.path.join(os.path.dirname(exdirs[sim.idxsim]), "mod3reslt.txt"), - no_vsc_low_k_bud_last, - ) - elif sim.idxsim == 3: - with_vscoff_bud_last = np.array(vals_to_store) - np.savetxt( - os.path.join(os.path.dirname(exdirs[sim.idxsim]), "mod4reslt.txt"), - with_vscoff_bud_last, - ) - - # if all 4 models have run, check relative results - if sim.idxsim == 2: - f1 = os.path.join(os.path.dirname(exdirs[sim.idxsim]), "mod1reslt.txt") - if os.path.isfile(f1): - no_vsc_bud_last = np.loadtxt(f1) - os.remove(f1) - - f2 = os.path.join(os.path.dirname(exdirs[sim.idxsim]), "mod2reslt.txt") - if os.path.isfile(f2): - with_vsc_bud_last = np.loadtxt(f2) - os.remove(f2) - - f3 = os.path.join(os.path.dirname(exdirs[sim.idxsim]), "mod3reslt.txt") - if os.path.isfile(f3): - no_vsc_low_k_bud_last = np.loadtxt(f3) - os.remove(f3) - - # model1_exit = no_vsc_bud_last[:, 2].sum() - # model2_exit = with_vsc_bud_last[:, 2].sum() - # model3_exit = no_vsc_low_k_bud_last[:, 2].sum() - - # Ensure models 1 & 2 give nearly identical flow results - # for each cell-to-cell exchange between columns 5 and 6 assert np.allclose( - no_vsc_bud_last[:, 2], with_vsc_bud_last[:, 2], atol=1e-3 + with_vsc_bud_last[:, 2], stored_ans[:, 2], atol=1e-3 ), ( "Flow in models " - + exdirs[0] - + " and " + exdirs[1] - + " should be approximately equal, but are not." + + " and the established answer should be approximately " + "equal, but are not." ) + elif sim.idxsim == 2: + no_vsc_low_k_bud_last = np.array(vals_to_store) + # Ensure the cell-to-cell flow between columns 5 and 6 in model - # 3 is less than what's in model 2 - assert np.less( - no_vsc_low_k_bud_last[:, 2], with_vsc_bud_last[:, 2] - ).all(), ( - "Exit flow from model " - + exdirs[1] - + " should be greater than flow existing " + # 3 is less than what's in the "with viscosity" model + assert np.less(no_vsc_low_k_bud_last[:, 2], stored_ans[:, 2]).all(), ( + "Exit flow from model the established answer " + "should be greater than flow existing " + exdirs[2] + ", but it is not." ) From bfbf4f2b848f2fc7aea166b637b928b189c7830a Mon Sep 17 00:00:00 2001 From: Eric Morway Date: Mon, 14 Nov 2022 16:42:27 -0800 Subject: [PATCH 30/31] oy vey --- autotest/test_gwf_vsc04_lak.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/autotest/test_gwf_vsc04_lak.py b/autotest/test_gwf_vsc04_lak.py index 6978fc0df7a..54757eca6e5 100644 --- a/autotest/test_gwf_vsc04_lak.py +++ b/autotest/test_gwf_vsc04_lak.py @@ -475,7 +475,7 @@ def build_model(idx, dir): ) # pull in the tabfile defining the lake stage, vol, & surface area - fname = os.path.join("data", "vsc04-laktab", "stg-vol-surfarea.csv") + fname = os.path.join("data", "vsc04-laktab", "stg-vol-surfarea.dat") tabinput = [] with open(fname, "r") as f: # peel off the hdr line From cb641cf4d0690d450fc2f10100598d02cb104ac5 Mon Sep 17 00:00:00 2001 From: "Langevin, Christian D" Date: Tue, 15 Nov 2022 10:43:08 -0600 Subject: [PATCH 31/31] few minor polishes on VSC --- doc/mf6io/gwf/vsc.tex | 4 ++-- doc/mf6io/mf6io.bbl | 8 +++++++- doc/mf6io/mf6ivar/dfn/gwf-vsc.dfn | 17 +++++++++++++---- doc/mf6io/mf6ivar/md/mf6ivar.md | 15 ++++++++------- doc/mf6io/mf6ivar/tex/gwf-vsc-desc.tex | 10 ++++++---- doc/mf6io/mf6ivar/tex/gwf-vsc-options.dat | 1 + src/Model/GroundWaterFlow/gwf3vsc8.f90 | 11 ++++++++--- 7 files changed, 45 insertions(+), 21 deletions(-) diff --git a/doc/mf6io/gwf/vsc.tex b/doc/mf6io/gwf/vsc.tex index 1a1e41270cc..9a1f3857481 100644 --- a/doc/mf6io/gwf/vsc.tex +++ b/doc/mf6io/gwf/vsc.tex @@ -16,7 +16,7 @@ \mu = \mu_T(T) + \sum_{i=1}^{NVISCSPECIES} DVISCDC_i \left ( CONCENTRATION_i - CVISCREF_i \right ) \end{equation} -where the first term on the right-hand side, $\mu_T(T)$, is a nonlinear function of temperature, and the summation corresponds to the summation in equation \ref{eqn:visclinear}, in which one of the ``species'' is heat. The nonlinear term in equation \ref{eqn:viscnonlinear} is of the form +\noindent where the first term on the right-hand side, $\mu_T(T)$, is a nonlinear function of temperature, and the summation corresponds to the summation in equation \ref{eqn:visclinear}, in which one of the ``species'' is heat. The nonlinear term in equation \ref{eqn:viscnonlinear} is of the form \begin{equation} \label{eqn:munonlinear} @@ -50,7 +50,7 @@ \subsubsection{Stress Packages} LAK & A VISCOSITY auxiliary variable or one or more auxiliary variables for calculating viscosity in the equation of state can be specified \\ SFR & A VISCOSITY auxiliary variable or one or more auxiliary variables for calculating viscosity in the equation of state can be specified \\ MAW & A VISCOSITY auxiliary variable or one or more auxiliary variables for calculating viscosity in the equation of state can be specified \\ -UZF & Pending ... \\ +UZF & Viscosity variations not implemented \\ \end{longtable} \vspace{5mm} diff --git a/doc/mf6io/mf6io.bbl b/doc/mf6io/mf6io.bbl index 05fffcda052..c4d7f4a09b6 100644 --- a/doc/mf6io/mf6io.bbl +++ b/doc/mf6io/mf6io.bbl @@ -1,4 +1,4 @@ -\begin{thebibliography}{34} +\begin{thebibliography}{35} \providecommand{\natexlab}[1]{#1} \expandafter\ifx\csname urlstyle\endcsname\relax \providecommand{\doi}[1]{doi:\discretionary{}{}{}#1}\else @@ -228,6 +228,12 @@ Prudic, D.E., Konikow, L.F., and Banta, E.R., 2004, A New Streamflow-Routing {U.S. Geological Survey Open File Report 2004--1042, 104 p.}, accessed June 27, 2017, at \url{https://pubs.er.usgs.gov/publication/ofr20041042}. +\bibitem[{Voss(1984)}]{Voss1984sutra} +Voss, C.I., 1984, SUTRA---A finite-element simulation model for + saturated-unsaturated fluid-density-dependent ground-water flow with energy + transport or chemically-reactive single-species solute transport: {U.S. + Geological Survey Water-Resources Investigations Report 84--4369, 409 p.} + \bibitem[{Zheng(2010)}]{zheng2010supplemental} Zheng, Chunmiao, 2010, MT3DMS v5.3, Supplemental User's Guide: {Technical Report Prepared for the U.S. Army Corps of Engineers, 51 p.} diff --git a/doc/mf6io/mf6ivar/dfn/gwf-vsc.dfn b/doc/mf6io/mf6ivar/dfn/gwf-vsc.dfn index fa11bda5bff..9e3ab6d0083 100644 --- a/doc/mf6io/mf6ivar/dfn/gwf-vsc.dfn +++ b/doc/mf6io/mf6ivar/dfn/gwf-vsc.dfn @@ -9,6 +9,15 @@ longname reference viscosity description fluid reference viscosity used in the equation of state. This value is set to 1.0 if not specified as an option. default_value 1.0 +block options +name temperature_species_name +type string +shape +reader urword +optional true +longname auxspeciesname that corresponds to temperature +description string used to identify the auxspeciesname in PACKAGEDATA that corresponds to the temperature species. There can be only one occurrence of this temperature species name in the PACKAGEDATA block or the program will terminate with an error. This value has no effect if viscosity does not depend on temperature. + block options name thermal_formulation type string @@ -17,7 +26,7 @@ reader urword optional true valid linear nonlinear longname keyword to specify viscosity formulation for the temperature species -description may be used for specifying which viscosity formulation to use for a species identified by the auxilary name TEMPERATURE. Can be either LINEAR or NONLINEAR. The LINEAR viscosity formulation is the default. +description may be used for specifying which viscosity formulation to use for the temperature species. Can be either LINEAR or NONLINEAR. The LINEAR viscosity formulation is the default. block options name thermal_a2 @@ -131,7 +140,7 @@ tagged false in_record true reader urword longname slope of the line that defines the linear relationship between viscosity and temperature or between viscosity and concentration, depending on the type of species entered on each line. -description real value that defines the slope of the line defining the linear relationship between viscosity and temperature or between viscosity and concentration, depending on the type of species entered on each line. If the value of AUXSPECIESNAME entered on a line is TEMPERATURE, this value will be used when VISCOSITY\_FUNC is equal to LINEAR (the default) in the OPTIONS block. When VISCOSITY\_FUNC is set to NONLINEAR, a value for DVISCDC must be specified though it is not used. +description real value that defines the slope of the line defining the linear relationship between viscosity and temperature or between viscosity and concentration, depending on the type of species entered on each line. If the value of AUXSPECIESNAME entered on a line corresponds to TEMPERATURE\_SPECIES\_NAME (in the OPTIONS block), this value will be used when VISCOSITY\_FUNC is equal to LINEAR (the default) in the OPTIONS block. When VISCOSITY\_FUNC is set to NONLINEAR, a value for DVISCDC must be specified though it is not used. block packagedata name cviscref @@ -141,7 +150,7 @@ tagged false in_record true reader urword longname reference temperature value or reference concentration value -description real value that defines the reference temperature or reference concentration value used for this species in the viscosity equation of state. If AUXSPECIESNAME entered on a line is TEMPERATURE, then CVISCREF refers to a reference temperature, otherwise it refers to a reference concentration. +description real value that defines the reference temperature or reference concentration value used for this species in the viscosity equation of state. If AUXSPECIESNAME entered on a line corresponds to TEMPERATURE\_SPECIES\_NAME (in the OPTIONS block), then CVISCREF refers to a reference temperature, otherwise it refers to a reference concentration. block packagedata name modelname @@ -151,7 +160,7 @@ tagged false shape reader urword longname modelname -description name of a GWT (or eventuallky a GWE) model used to simulate a species that will be used in the viscosity equation of state. This name will have no effect if the simulation does not include a GWT model that corresponds to this GWF model. +description name of a GWT model used to simulate a species that will be used in the viscosity equation of state. This name will have no effect if the simulation does not include a GWT model that corresponds to this GWF model. block packagedata name auxspeciesname diff --git a/doc/mf6io/mf6ivar/md/mf6ivar.md b/doc/mf6io/mf6ivar/md/mf6ivar.md index 61613c55a15..4423337ddb1 100644 --- a/doc/mf6io/mf6ivar/md/mf6ivar.md +++ b/doc/mf6io/mf6ivar/md/mf6ivar.md @@ -147,9 +147,9 @@ | GWF | DISV | DIMENSIONS | NLAY | INTEGER | is the number of layers in the model grid. | | GWF | DISV | DIMENSIONS | NCPL | INTEGER | is the number of cells per layer. This is a constant value for the grid and it applies to all layers. | | GWF | DISV | DIMENSIONS | NVERT | INTEGER | is the total number of (x, y) vertex pairs used to characterize the horizontal configuration of the model grid. | -| GWF | DISV | GRIDDATA | TOP | DOUBLE PRECISION (NCPL, 1) | is the top elevation for each cell in the top model layer. | -| GWF | DISV | GRIDDATA | BOTM | DOUBLE PRECISION (NCPL, 1, NLAY) | is the bottom elevation for each cell. | -| GWF | DISV | GRIDDATA | IDOMAIN | INTEGER (NCPL, 1, NLAY) | is an optional array that characterizes the existence status of a cell. If the IDOMAIN array is not specified, then all model cells exist within the solution. If the IDOMAIN value for a cell is 0, the cell does not exist in the simulation. Input and output values will be read and written for the cell, but internal to the program, the cell is excluded from the solution. If the IDOMAIN value for a cell is 1 or greater, the cell exists in the simulation. If the IDOMAIN value for a cell is -1, the cell does not exist in the simulation. Furthermore, the first existing cell above will be connected to the first existing cell below. This type of cell is referred to as a ``vertical pass through'' cell. | +| GWF | DISV | GRIDDATA | TOP | DOUBLE PRECISION (NCPL) | is the top elevation for each cell in the top model layer. | +| GWF | DISV | GRIDDATA | BOTM | DOUBLE PRECISION (NCPL, NLAY) | is the bottom elevation for each cell. | +| GWF | DISV | GRIDDATA | IDOMAIN | INTEGER (NCPL, NLAY) | is an optional array that characterizes the existence status of a cell. If the IDOMAIN array is not specified, then all model cells exist within the solution. If the IDOMAIN value for a cell is 0, the cell does not exist in the simulation. Input and output values will be read and written for the cell, but internal to the program, the cell is excluded from the solution. If the IDOMAIN value for a cell is 1 or greater, the cell exists in the simulation. If the IDOMAIN value for a cell is -1, the cell does not exist in the simulation. Furthermore, the first existing cell above will be connected to the first existing cell below. This type of cell is referred to as a ``vertical pass through'' cell. | | GWF | DISV | VERTICES | IV | INTEGER | is the vertex number. Records in the VERTICES block must be listed in consecutive order from 1 to NVERT. | | GWF | DISV | VERTICES | XV | DOUBLE PRECISION | is the x-coordinate for the vertex. | | GWF | DISV | VERTICES | YV | DOUBLE PRECISION | is the y-coordinate for the vertex. | @@ -792,7 +792,8 @@ | GWF | OC | PERIOD | FREQUENCY | INTEGER | save at the specified time step frequency. This keyword may be used in conjunction with other keywords to print or save results for multiple time steps. | | GWF | OC | PERIOD | STEPS | INTEGER (] + [TEMPERATURE_SPECIES_NAME ] [THERMAL_FORMULATION ] [THERMAL_A2 ] [THERMAL_A3 ] diff --git a/src/Model/GroundWaterFlow/gwf3vsc8.f90 b/src/Model/GroundWaterFlow/gwf3vsc8.f90 index e9c91ba47a1..e5f3a6fbf20 100644 --- a/src/Model/GroundWaterFlow/gwf3vsc8.f90 +++ b/src/Model/GroundWaterFlow/gwf3vsc8.f90 @@ -44,6 +44,7 @@ module GwfVscModule real(DP), dimension(:), pointer, contiguous :: ctemp => null() !< temporary array of size (nviscspec) to pass to calc_visc_x character(len=LENMODELNAME), dimension(:), allocatable :: cmodelname !< names of gwt (or gwe) models used in viscosity equation character(len=LENAUXNAME), dimension(:), allocatable :: cauxspeciesname !< names of aux columns used in viscosity equation + character(len=LENAUXNAME) :: name_temp_spec = 'TEMPERATURE' ! ! -- Viscosity constants real(DP), pointer :: a2 => null() !< an empirical parameter specified by the user for calculating viscosity @@ -1142,11 +1143,11 @@ subroutine read_packagedata(this) call this%parser%GetStringCaps(this%cmodelname(iviscspec)) call this%parser%GetStringCaps(this%cauxspeciesname(iviscspec)) ! - if (this%cauxspeciesname(iviscspec) == 'TEMPERATURE') then + if (this%cauxspeciesname(iviscspec) == this%name_temp_spec) then if (this%idxtmpr > 0) then write (errmsg, '(a)') 'More than one species in VSC input identified & - &as "TEMPERATURE". Only one species may be designated as & - &"TEMPERATURE".' + &as '//trim(this%name_temp_spec)//'. Only one species may be & + &designated to represent temperature.' call store_error(errmsg) else this%idxtmpr = iviscspec @@ -1403,6 +1404,10 @@ subroutine read_options(this) 'followed by FILEOUT' call store_error(errmsg) end if + case ('TEMPERATURE_SPECIES_NAME') + call this%parser%GetStringCaps(this%name_temp_spec) + write (this%iout, '(4x, a)') 'Temperature species name set to: '// & + trim(this%name_temp_spec) case ('THERMAL_FORMULATION') call this%parser%GetStringCaps(keyword2) if (trim(adjustl(keyword2)) == 'LINEAR') this%thermivisc = 1